Showing posts with label utilities. Show all posts
Showing posts with label utilities. Show all posts

Thursday, 29 October 2015

Arduino: Move 4 Stepper Motors Synchronously

...but, theoretically, it would work for an n number of motors.

Part of the machine inside FreeCAD
This is the algorithm that plans the trajectory and syncs the movements of the stepper motors, in the new CNC machine that I'm designing (and building!)

The input is an int array { MA, MB, MC, MD } where each int means the number of steps (positive or negative).

The stepper movement logic is stored into a boolean[8] array { SA, DA, SB, DB.... } where SA means step for motor A and DA is the direction bit of the motor A (for a 2 wire stepper driver, like the a4988). This array is erased and re-computed every loop cycle with the next movement, so real motor stepping has to take place inside this loop.

The image below (created with FreeCAD) shows a representation of the algorithm output for a requested movement of ( 5, 15, 25, 40 ) steps.

Input ( 5, 15, 25, 40 ), step bit only

It would be possible to do some kind of buffer by translating that boolean array into an int inside an int array. This should be a more-less memory efficient way of decoupling motion logic and motion execution, something to try in a future.

The code:

/*
 * JMG October 2015
 * NiCr Stepper Sync Algorithm V1
 * Tested on Arduino Nano (Ide 1:635)
 * 
 */

// stepper_instruction { Step1A, Dir1A, Step1B, Dir1B, Step2A, Dir2A, Step2B, Dir2B }
bool stepper_instruction[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };

void moveAB( int Adx, int Ady, int Bdx, int Bdy )
{
  int delta[4] = { Adx, Ady, Bdx, Bdy };
  int aux = 0;
  for( int i = 0; i < 4; i++ )
  {
    if( abs(delta[i]) > aux )
    {
      aux = abs(delta[i]);
    }
  }
  float R[4] = { 0, 0, 0, 0 };
  for( int i = 0; i < 4; i++ )
  {
    R[i] = (float)delta[i] / (float)aux;
  }
  int inc[4] = { 0, 0, 0, 0 };
  int acc[4] = { 0, 0, 0, 0 };
  int j = 0;
  while( ( acc[0] != Adx )||( acc[1] != Ady )||( acc[2] != Bdx )||( acc[3] != Bdy ) )
  {
    j++;
    for( int i = 0; i < 4; i++ )
    {
      inc[i] = round( R[i]*j - acc[i] );
      acc[i] = acc[i] + inc[i];
      stepper_instruction[2*i] = abs( inc[i] );
      if( inc[i] < 0 ) { stepper_instruction[2*i+1] = 1; }
      else { stepper_instruction[2*i+1] = 0; }
    }
    for( int i = 0; i < 7; i++ )
    {
      Serial.print( stepper_instruction[i] );
    }
    Serial.println( stepper_instruction[7] );
    for( int i = 0; i < 8; i++ )
    {
      stepper_instruction[i] = 0;
    }
  }
  for( int i = 0; i < 4; i++ )
  {
    Serial.print( acc[i] );
  }
  Serial.println();
  for( int i = 0; i < 4; i++ )
  {
    Serial.print( delta[i] );
  }
}
void setup()
{
  Serial.begin( 115200 );
  delay( 500 );
  moveAB( 100, -5000, 780, 25 );
}
void loop()
{
}

It prints by serial (115200 baud) the values of stepper_instruction at each loop cycle, and the counted steps vs the requested steps at the end of the loop.
I'm sure this is not the optimal way of doing it, but it works.

Update: the algorithm in action

PS:
I will reveal more info about the machine soon. NiCr

Friday, 23 October 2015

Arduino: Read Instruction From Serial

When using Arduino, parsing serial data and converting it to something usable is way harder than it seems.


While this functionality is specially useful as it allows your computer to talk with the Arduino easily, a quick search on google will give you thousands of very different solutions. The one exposed here is just another one that is working perfectly for me.

The instruction:

The idea is to set the position of three servos by serial and also be able to change other parameters.
The instruction, then, must be composed from an instruction name and the parameters, for example, to move the three servos:

MOVE 120 23 90

And, if we also want to switch on a light, the instruction could be:

LIGHT ON

How do we get the Arduino to know that we want the servos to move and then switch on a light?

 

Arduino Read Instruction:

Code:
/*
JMG OCTOBER 2015
This script is made up from snippets found in stackoverflow and other
google results
*/

void setup()
{
  // open serial port at 115200 baud
  Serial.begin(115200);
}

String complete_instruction[4];  // will contain the decoded instruction (4 fields)
void loop()
{
  
  while(!Serial.available()) {}  // if there is nothing on serial, do nothing
  int  i = 0;
  char raw_instruction[25];
  while (Serial.available())
  {  // if something comes from serial, read it and store it in raw_instruction char array
    delay(10); // delay to allow buffer to fill
    if (Serial.available() > 0)
    {
      raw_instruction[i] = Serial.read();
      i++;
    }
  }
  if( strlen( raw_instruction ) > 0 )  // if a new raw_instruction has been read
  {
    // clean raw_instruction before decoding (overwrite non filled array positions with empty spaces)
    for( int n = i; n < 25; n++ ) { raw_instruction[n] = ' '; }
    // decode the instruction (4 fields) (iterator n = field, iterator j = character)
    int j = 0;
    for( int n = 0; n < 4; n++ )
    { 
      while( j < 25 )
      {
        if( raw_instruction[j] == ' ' )
        {
          j++;
          break;
        }
        else
        {
          complete_instruction[n] += raw_instruction[j];
        }
        j++;
      }
    }
    // print decoded instruction by serial
    for( int n = 0; n<4; n++ )
    {
      Serial.println( complete_instruction[n] );
      // clear field after using it
      complete_instruction[n]="";
    }
  }
  delay(50);
}

The summary is that you can send by serial something like "A B C D" (using space as delimiter) and the variable complete_instruction will contain {A, B, C, D}. A,B,C,D can be any kind of data of any length you want, for the instruction MOVE 120 23 90, it will return {"MOVE", "120", "23", "90"}. For the second instruction LIGHT ON, it will return {"LIGHT", "ON", "", "" }.

This way, now you just need to set several if conditions to check if the instruction says "MOVE", "LIGHT", "KILL" or whatever, and if it does, check the remaining parameters (you can use toInt() or toFloat() for numeric values).

My experience with this script is that it works as intended and does not cause memory problems. But I do not have enough knowledge about C to assure that it will not crash your script (from what I've read, String variables together with low RAM space seem to give problems.) Just take it into account if something very weird is happening. (code updated, memory problem solved?)


Bye!

Thursday, 10 September 2015

FreeCAD: Intersection Between Shapes

Hi!

This is a small script that will find intersection volumes between the selected shapes, for example, given two cubes and a sphere, this is the result:


It gives some transparency to the original shapes and also displays intersection volume at the report view.

The code is:

# JMG 2015
object_list = []
for obj in FreeCAD.Gui.Selection.getSelectionEx():
  obj = obj.Object
  object_list.append( obj )

for n in range( len(object_list) ):
  object_A = object_list[n]
  for i in range( len(object_list) ):
    if i <= n:
      pass
    
    else:
      object_B = object_list[i]
      common = object_A.Shape.common( object_B.Shape )
      if common.Volume > 0.0:
        FreeCAD.Console.PrintMessage( '-Intersection- ' + object_A.Name + ' with ' + object_B.Name + '\n')
        FreeCAD.Console.PrintMessage( 'Common volume: ' + str( common.Volume ) + '\n' + '\n' )
        
        intersection_object = FreeCAD.ActiveDocument.addObject( 'Part::Feature', 'Intersection_Volume' )
        intersection_object.Shape = common
        intersection_object.ViewObject.ShapeColor = ( 1.0,0.0,0.0,1.0 )
        object_A.ViewObject.Transparency = 80
        object_B.ViewObject.Transparency = 80

Saturday, 18 July 2015

FreeCAD: Drill macro

I am working on a project that has a lot of drill operations. Being an "assembly", it can get quite tedious to create an sketch or part cylinder, place it and then do a boolean cut to make the drill. To speed it up, I have written a short macro that drills existing holes into the selected shapes:

 

Selection:


 

Script:

"""
Drill Macro
JMG 2015 GPL

Select a set of circles and then the shapes that you want to drill
"""

#get user selection of edges
SelEdges = Gui.Selection.getSelectionEx()[0].SubObjects  

# get user selection for shapes
SelectedShapes = Gui.Selection.getSelectionEx() 

# create the faces that will be extruded to create the drill
cutFaces = []
for edge in SelEdges:
  cutFace = Part.Face(Part.Wire(edge)) # create face from edge
  cutFaces.append( cutFace ) 

# drill all selected shapes minus first one (selected edges)
for i in range(len(SelectedShapes)-1): 
  #retrieve object from selection list
  SelShape = SelectedShapes[i+1].Object 

  #create an empty part object in the document
  drilledShape=FreeCAD.ActiveDocument.addObject("Part::Feature",'Drilled'+SelShape.Name)
  drilledShape.Shape = SelShape.Shape
  #cut selected shape and assign shape to "drilledShape"
  for f in cutFaces:
    drilledShape.Shape=drilledShape.Shape.cut(f.extrude(f.normalAt(0,0)*1000000))
    drilledShape.Shape=drilledShape.Shape.cut(f.extrude(f.normalAt(0,0)*-1000000))
  
  #turn off visibility of the selected shape and set same color for the new object
  SelShape.ViewObject.Visibility = False
  drilledShape.ViewObject.ShapeColor = SelShape.ViewObject.ShapeColor

Output:



Also, if you want to drill something more complex, like a slot, a variation of the script can do it:

Selection:

Script:
"""
Drill Edges Macro
JMG 2015 GPL

Select a set of edges that form a closed wire and then the shapes
that you want to drill
"""

#get user selection of edges
SelEdges = Gui.Selection.getSelectionEx()[0].SubObjects  

# get user selection for shapes
SelectedShapes = Gui.Selection.getSelectionEx() 

# create the face that will be extruded to create the drill
cutFace = Part.Face(Part.Wire( SelEdges )) 

# drill all selected shapes minus first one (selected edges)
for i in range(len(SelectedShapes)-1): 
    #retrieve object from selection list
    SelShape = SelectedShapes[i+1].Object 
    
    #create an empty part object in the document
    drilledShape=FreeCAD.ActiveDocument.addObject("Part::Feature",'Drilled'+SelShape.Name)
    
    #cut selected shape and assign shape to "drilledShape"
    drilledShape.Shape=SelShape.Shape.cut(cutFace.extrude(cutFace.normalAt(0,0)*1000000))
    drilledShape.Shape=drilledShape.Shape.cut(cutFace.extrude(cutFace.normalAt(0,0)*-1000000))
    
    #turn off visibility of the selected shape and set same color for the new object
    SelShape.ViewObject.Visibility = False
    drilledShape.ViewObject.ShapeColor = SelShape.ViewObject.ShapeColor

Result:




I have them in a custom toolbar and they have helped me to do things faster.

Maybe it helps you too :)

Bye!

Monday, 20 April 2015

FreeCAD: Sticker Sketch and Text

Sometimes you need to model a special part like a shift drum for a motorcycle gearbox or something more simple like putting text on your perfectly modelled cup of coffee. While the content of this post does not completely solve any of this problems, it shows an important step forward. I'm doing this because of its similarity with some parts of the sheet metal workbench core.

What it does is to roll around a cylindrical surface a sketch or a text string. Code at the end of the post.

Sketch sticker:

This is a plain sketch tangent to a cylinder, with a random polygon inside:

 

The script rolls the sketch along the cylinder surface. Only straight lines at the moment.

 

Text sticker:

Equal to the previous one but with strings. The font must must be made only of straight lines.



It works for any face orientation (but not character length, this will be improved )


Text Sticker Code:


"""
Javier Martinez Garcia  2015  GPL
"Text sticker" script
"""

import Draft
from math import sin, cos, pi

BaseCylinder = Gui.Selection.getSelectionEx()[0].SubObjects[0]

# Cylinder axis:
for edge in BaseCylinder.Edges:
  if str(edge.Curve)[0] == "C":
    AxisPoint = edge.Curve.Center
    AxisDirection = edge.Curve.Axis
    CylinderRadius = edge.Curve.Radius
    break

# For text string:
FaceList = []
SelStrings = Gui.Selection.getSelection()[1].Shape.Wires
wire0 = SelStrings[0]
p1 = wire0.Edges[0].Curve.StartPoint
p2 = wire0.Edges[0].Curve.EndPoint
p3 = wire0.Edges[1].Curve.EndPoint
va = p1 - p2
vb = p3 - p2
SketchNormal = ( va.cross( vb ) ).normalize()
TangencyPoint = AxisPoint + SketchNormal*CylinderRadius
CylRevPlane = ( SketchNormal.cross( AxisDirection ) ).normalize() # perpendicular

for wire in SelStrings:
  def H( n, m ):
    #Hamilton product
    w = n[0]*m[0] - n[1]*m[1] - n[2]*m[2] - n[3]*m[3]
    i = n[0]*m[1] + n[1]*m[0] + n[2]*m[3] - n[3]*m[2]
    j = n[0]*m[2] - n[1]*m[3] + n[2]*m[0] + n[3]*m[1]
    k = n[0]*m[3] + n[1]*m[2] - n[2]*m[1] + n[3]*m[0]
    return ( w, i, j, k )

  def RotateVector( V, Axis, alpha ):
    #Rotate 3d vector with axis and angle using quaternions
    csa2 = cos( alpha / 2.0 )
    ssa2 = sin( alpha / 2.0 )
    R = ( csa2, ssa2*Axis[0], ssa2*Axis[1], ssa2*Axis[2] )
    RT = ( csa2, -ssa2*Axis[0], -ssa2*Axis[1], -ssa2*Axis[2] )
    V = ( 0, V[0], V[1], V[2] )
    RV = H( H( R, V ), RT )
    return ( RV[1], RV[2], RV[3] )

  #projectpointocylinder
  def point2Cyl(p):
    # input p = FreeCAD.Vector( x, y, z )
    L0 = FreeCAD.Vector(p)
    L1 = FreeCAD.Vector(p)
    E_TGL_P0A = L0.projectToPlane( TangencyPoint, CylRevPlane )
    E_TGL_P0B = L1.projectToPlane( TangencyPoint, CylRevPlane ).projectToPlane( AxisPoint, SketchNormal )
    VBA = E_TGL_P0A - E_TGL_P0B 
    VBA_N = (E_TGL_P0A - E_TGL_P0B  ).normalize()
    ARC_RAD = ( p - E_TGL_P0A ).Length / CylinderRadius
    # Turn direction
    Aux_VRotA = FreeCAD.Vector( RotateVector( VBA_N, AxisDirection, ARC_RAD ) )
    Aux_VRotB = FreeCAD.Vector( RotateVector( VBA_N, AxisDirection*-1, ARC_RAD ) )
    if (Aux_VRotA - p ).Length > ( Aux_VRotB - p ).Length:
      VRot =  Aux_VRotB

    else:
      VRot = Aux_VRotA

    RP0 = FreeCAD.Vector(VRot).multiply( CylinderRadius ) + E_TGL_P0B
    return RP0
  
  
  WireBS = []
  for edge in wire.Edges:
    points = []
    for i in range(100):
      p = edge.valueAt( edge.Length*i / 100.0 )
      rp = point2Cyl(p)
      points.append( rp )

    rp = point2Cyl( edge.Curve.EndPoint )
    points.append( rp )

    SPL = Part.BSplineCurve()
    SPL.interpolate( points )
    SPL = SPL.toShape()
    WireBS.append( SPL )
  
  Face = Part.makeFilledFace( WireBS )
  FaceList.append( Face )


Compound = Part.Compound( FaceList )
Part.show( Compound )


To use it,  create a cylinder, place a string tangent to it; select the cylindrical surface and the text; run the script. Is very important that the font type of the text is made from straight lines, otherwise it will fail. The font I've used for testing is "UASquare".

Next step is to get it to work with circular edges.

UPDATE 1


The script for sketches is almost working, some screenshots:


Top view of the generated frame:


Generated frame:



This is the step that I'm trying to automatize:


Create faces from edges, create shell from faces, create solid from shell.


Bye!!

Friday, 14 November 2014

FreeCAD: Tour camera

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:

from pivy import coin
import time
from FreeCAD import Base
cam = FreeCADGui.ActiveDocument.ActiveView.getCameraNode()
trajectory = Gui.Selection.getSelectionEx()[0].Object.Shape.Edges
for edge in trajectory:
  startPoint = edge.valueAt( 0.0 )
  endPoint = edge.valueAt( edge.Length )
  dirVector = ( endPoint - startPoint ).normalize()
  currentPoint = startPoint
  while (currentPoint - startPoint).Length < edge.Length:
    currentPoint = currentPoint + dirVector
    cam.position.setValue(currentPoint + Base.Vector( 0,0, 10) )
    cam.pointAt( coin.SbVec3f( endPoint[0], endPoint[1], endPoint[2]+10) , coin.SbVec3f( 0, 0, 1 ) )
    Gui.updateGui()
    time.sleep(0.005)

What it does is:

-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 2014
from 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 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 )



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 November 2014
from pivy import coin
import time
from FreeCAD import Base
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 in RawTrajectory:
    if edge.valueAt(0.0) == currentEdge.valueAt( currentEdge.Length ):
      trajectory.append( edge )
      currentEdge = edge
      break

camHeight = 10   # Height of 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 )



Enjoy!

Saturday, 13 September 2014

FreeCAD: simple Coin3d plot

Hi all: I'm back!

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 in range(5):
  for n in range(-25,25):
    for i in range(-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)



More info about FreeCAD and Coin3D, here

Bye!

Saturday, 21 June 2014

PyQtGraph: Box scenery

This post is about creating something like...



...using pyqtgraph.

Why?

Because of a project involving solar-thermal panels, at the beginning of this year, I realized that the way of obtaining how much shadow-per-year-percentage an object projects over another object was very inaccurate. So I attempted  to create a sun-simulator, where you place the objects that are near to the place you want to study and then, run a script that draws shadows (with their grey color as a function of the % of shadow) over the place. That way, you can smartly place your solar collector.


I've not gone that further, I left the (short) development once I knew that something ~ similar existed for free. Don't reinvent the wheel.

But I want to show this, because maybe it helps you to go a step forward with your own project.

The code is posted here (there are comments explaining how to create "buildings" and "panels")

If you have pyqtgraph in your system, copy and paste the code in a python terminal and you should see something.

I've been playing today with the camera positions, with the remote idea of a video-game in mind.

Camera command (I haven't found this at pyqtgraph docs, but here) example:

WINDOWOBJECT.setCameraPosition(distance=100,elevation=10,azimuth=30)



Greetings, Javier





Thursday, 12 June 2014

Bash: Execute script in a terminal at startup

Today I was wondering how could I monitor the temperatures of my computer automatically, using something that runs at startup without doing anything manually.

This is the result:


A terminal with transparent background that shows the temps.

Code:

The code to display sensor values and update them two times per second is:

while [ 1 ]; do 
sensors;                                  # Outputs cpu and motherboard temps
aticonfig --odgt;                     # Outputs my AMD GPU temps.
sleep 0.5;                               # Delay for 0.5 seconds
done

Save it somewhere as .sh and give execution permissions.

To run the code above (or any other you want) in a terminal:

gnome-terminal --profile=cristal --zoom=0.85 --geometry=60x16+1920+0 -x PATH

I've set previously a new profile of gnome-terminal (called "cristal") that has transparent background and "Temperaturas" as window label.
Then, using the --profile option I set that profile for the new terminal.

The option --zoom=0.85 reduces the terminal size at 85%  (genius)

The parameter --geometry is more interesting:

Columns x Lines + Xposition + Yposition

The position is given by your screen resolution. On my screen, the window is in the upper right corner when X = 1920 and Y=0

And finally, PATH is the absolute path to the code you want to execute.


To execute at startup, go to "startup applications", add a new application and where it says "command", paste the line that starts "gnome-terminal --profile......" customized to suit your particulad needs.
Save it and you are done.

Bye!

Saturday, 31 May 2014

FreeCAD: Vertex to Vertex Positioning Function

FreeCAD 0.14 is taking too long to be released and, if when you try to compile it, everything is a non-sense and you end up feeling frustrated and stupid (as happens to me every time), you may like this:

Frustration solved: Hamish Assembly2 workbench here

Vector to Vector translation function:

 

Code:

from FreeCAD import Gui

def V2V():
  MouseSel = Gui.Selection.getSelectionEx()
  ObjA_Name = MouseSel[0].ObjectName
  PointA = MouseSel[0].SubObjects[0].Point
  PointB = MouseSel[1].SubObjects[0].Point
  Vector = PointB - PointA
  Pos0 = App.ActiveDocument.getObject(ObjA_Name).Placement.Base
  Rot0 = App.ActiveDocument.getObject(ObjA_Name).Placement.Rotation
  MVector = Pos0 + Vector
  App.ActiveDocument.getObject(ObjA_Name).Placement = App.Placement(MVector, Rot0)

 To Use it:

Copy-Paste at FreeCAD command line the code above and then:

-Select a point of the object you want to move
-Select a point of another object where you want the previous one to be placed
-Call the function (write V2V() at command line and Enter)

And you're done!


With this you can save some time when placing objects without the assembly workbench. 


Bye!

Friday, 30 May 2014

FreeCAD: Perforated Sheets and HoneyComb

Perforated sheets are very useful and widely used in a lot of things. Yesterday I started to think about the algorithm and this is the result:



Simple circular holes at the moment. Circular and Hexagonal now!





The code is here

The function PSheet has 6 inputs, 4 mandatory and 2 optional:

L = Length of the base rectangle
H = Heigth of the base rectangle
W = Width of the base rectangle
R = Hole radius

The optional parameters are hole type (circle by default) and  "hole density" set as 1/5 by default.

To get the same as the above photo type after copy-paste:

PSheet(100, 100, 1, 4)

Yes, is slow to load. I will try to improve that, maybe doing a big sketch with all the holes, extruding them and substracting to the main plane.

Now everything is created with sketches. The speed has improved but not enougth to create big sheets. Almost all time is spent doing the recompute step, the last line of the script. I don't know how to speed up that :s
This is the graph of the needed time as a function of the sheet length (with heigth equal to length):


Just for curiosity and to see that if you want to create something bigger than an 800x800mm sheet, you will need more than 24 hours.

Honeycomb :

If you give enougth width to an hexagonal pattern you get a honeycomb like this one:


Honeycomb cores are very common in carboon-fiber structures.

That's all by now.

Sunday, 4 May 2014

FreeCAD: Work Features Release 1

UPDATED: Thanks to the community contributions, this work features project has been extended with more options and a nice gui. Find it here: http://www.freecadweb.org/wiki/index.php?title=Macro_WorkFeatures

This is the first release of the Work Features macro. It can do what is shown in the video plus few things more.



Is not finished and I have to update some functions, but to know how it works, the code is here.

How to use it: 

(I don't know exactly why, but the last part of this section has the fonts messed up, sorry for that)
At the moment, you need to copy and paste the code at the FreeCAD python terminal every time you open a new document. 
Yes, I know I need to improve this. Any help is appreciated.  ;)

Once you do that, you need to move by command line (need to improve here too) and these are the options:

-OriginObjects This is executed automatically at copy-paste, generates Origin and folders
-WorkPoint:
-MiddPoint # Select one or more edges and will place the middpoint
-Center # Select one or more circular edges and will place centerpoint
-FaceCenter # Select one or more faces and will place a point at geometrical center
-WorkAxis:
-TwoPoints # Select two points and will place an axis across
-CylinderAxis # Select a cylindrical face and will place its axis
-WorkPlane:
-AxisandPoint # Select one axis and point and will place a plane containing both
-ThreePoints # Select three points and will place a plane with the points inside
-PlaneandPoint # Select one plane and point and will place a parallel plane at point
The workflow is:
Select the needed objects for reference and then type the command.
For example, one command could be:
WorkPoint.FaceCenter()
to create a point at the center of the selected face.
or
WorkPlane.ThreePoints()
to create a plane across three points.
And that's all!
Tell me your experience with the macro or any suggestions you have :)
Bye!

Tuesday, 29 April 2014

FreeCAD: Work-Features Faster Than Expected

(Update, work-features release 1 here)
A brief post to show the current state of the work-features implementation:


Things have gone faster than expected, but there remains a lot of work to do.

What do you think about the feature?


Greetings!

Monday, 28 April 2014

FreeCAD: Work-Features, Origin

After working a bit more in the work-points script I realized that is obligatory to have origin work-features, as origin of coordinates point, X, Y, Z axis, XY, XZ and YZ planes.

A screenshot to clarify:



The colors of the axis are to distinguish them, and the origin work-planes colors are different from the future user-created work-plane colors. 

Everything is ordered in the tree-view by name and invisible by default:




I'm trying to figure how to use Github, I think I have posted the code here

To use it, with a part or new document opened, copy-paste the code, that's all. 


Bye!