13. Cnc25D Design Details

13.1. Cnc25D design usage

13.1.1. From the source repository

13.1.1.1. Using the design module

Go to the Cnc25D source repository and execute the design script with or without arguments:

> cd Cnc25D
> python cnc25D/XYZDesign.py

or:

> python cnc25D/XYZDesign.py --param_A 50.0 --param_C 30.0

Without arguments, the default command line is used.

When you don’t use argument, you can also use freecad instead of python

> freecad cnc25D/XYZDesign.py

With freecad, you can not choose the arguments on the command line because of the conflict with the freecad argument parser. So you have to change the default command line at the end of the design script:

if __name__ == "__main__":
  FreeCAD.Console.PrintMessage("XYZDesign.py says hello!\n")
  my_xyz = XYZDesign_cli("--param_A 6.0 --param_B 13.0 --return_type freecad_object".split()) # default command line arguments: choose here you argument to run the script with freecad
  try: # depending on xyz_c['return_type'] it might be or not a freecad_object
    Part.show(my_xyz)
    print("freecad_object returned")
  except:
    pass
    #print("return_type is not a freecad-object")

The argument –return_type freecad_object lets you visualizing the result in FreeCAD.

13.1.1.2. Using the test-macro

Go to the Cnc25D source repository and execute the test-macro without argument:

> cd Cnc25D
> python cnc25D/tests/XYZDesign_macro.py

or:

> freecad cnc25D/tests/XYZDesign_macro.py

You can use those test-macro scripts as FreeCAD macro and run them from the FreeCAD GUI. Make sure the test-macro script returns a freecad_object:

xyz_x['return_type'] = 'freecad_object'

13.1.2. From the installed Cnc25D package

After installing the Cnc25D Python package, run cnc25d_example_generator.py to get the Cnc25D example scripts. These Cnc25D example scripts are actually a copy of the previous test-macros. You can execute them without argument with python or freecad:

> cd where/I/have/generated/the/Cnc25D/example/scripts
> python eg05_XYZDesign_example.py

or:

> freecad eg05_XYZDesign_example.py

Like with the test-macro script, make sure the script returns a freecad_object. If not, edit your script and set the following constraint:

xyz_x['return_type'] = 'freecad_object'

Your script can also be used as a FreeCAD macro and can be called from the FreeCAD GUI.

13.2. Cnc25D design implementation structure

Template of a Cnc25D design script:

################################################################
# import
################################################################

import cnc25d_api
cnc25d_api.importing_freecad()
import math
import sys, argparse
import Part

################################################################
# XYZDesign dictionary-constraint-arguments default values
################################################################

def XYZDesign_dictionary_init():
  """ create and initiate a XYZDesign_dictionary with the default value
  """
  r_xyzd = {}
  r_xyzd['param_A'] = 5.0
  r_xyzd['param_B'] = 10.0
  r_xyzd['param_C'] = 0.0
  r_xyzd['return_type'] = 'int_status' # possible values: 'int_status', 'cnc25d_figure', 'freecad_object'
  # ...
  return(r_xyzd)

################################################################
# XYZDesign argparse
################################################################

def XYZDesign_add_argument(ai_parser):
  """
  Add arguments relative to the XYZDesign
  This function intends to be used by the XYZDesign_cli and XYZDesign_self_test
  """
  r_parser = ai_parser
  r_parser.add_argument('--param_A','--pa', action='store', type=float, default=5.0, dest='sw_param_A',
    help="Set the param_A. Default: 5.0")
  r_parser.add_argument('--param_B','--pb', action='store', type=float, default=10.0, dest='sw_param_B',
    help="Set the param_B. Default: 10.0")
  r_parser.add_argument('--param_C','--pc', action='store', type=float, default=0.0, dest='sw_param_C',
    help="Set the param_C. If equal to 0.0, the default value is computed. Default: 0.0")
  # ...
  return(r_parser)

################################################################
# the most important function to be used in other scripts
################################################################

def XYZDesign(ai_constraints):
  """
  The main function of the script.
  It generates a XYZDesign according to the constraint-arguments
  """
  ### check the dictionary-arguments ai_constraints
  xyzdi = XYZDesign_dictionary_init()
  xyz_c = xyzdi.copy()
  xyz_c.update(ai_constraints)
  if(len(xyz_c.viewkeys() & xyzdi.viewkeys()) != len(xyz_c.viewkeys() | xyzdi.viewkeys())): # check if the dictionary xyz_c has exactly all the keys compare to XYZDesign_dictionary_init()
    print("ERR157: Error, xyz_c has too much entries as {:s} or missing entries as {:s}".format(xyz_c.viewkeys() - xyzdi.viewkeys(), xyzdi.viewkeys() - xyz_c.viewkeys()))
    sys.exit(2)
  ### dynamic default value
  if(ai_constraints['param_C']==0):
    xyz_c['param_C'] = xyz_c['param_B']/5

  ### generate the XYZDesign figure
  # ...

  # display with Tkinter
  if(xyz_c['tkinter_view']):
    print(XYZDesign_parameter_info)
    cnc25d_api.figure_simple_display(xyz_figure, xyz_figure_overlay, XYZDesign_parameter_info)
  # generate output file
  cnc25d_api.generate_output_file(xyz_figure, xyz_c['output_file_basename'], xyz_c['XYZDesign_height'], XYZDesign_parameter_info)

  #### return
  if(xyz_c['return_type']=='int_status'):
    r_xyz = 1
  elif(xyz_c['return_type']=='cnc25d_figure'):
    r_xyz = xyz_figure
  elif(xyz_c['return_type']=='freecad_object'):
    r_xyz = cnc25d_api.figure_to_freecad_25d_part(xyz_figure, xyz_c['XYZDesign_height'])
  else:
    print("ERR508: Error the return_type {:s} is unknown".format(xyz_c['return_type']))
    sys.exit(2)
  return(r_xyz)

################################################################
# XYZDesign wrapper dance
################################################################

def XYZDesign_argparse_to_dictionary(ai_xyz_args):
  """ convert a XYZDesign_argparse into a XYZDesign_dictionary
  """
  r_xyzd = {}
  r_xyzd['param_A']  = ai_xyz_args.sw_param_A
  r_xyzd['param_B']  = ai_xyz_args.sw_param_B
  r_xyzd['param_C']  = ai_xyz_args.sw_param_c
  #### return
  return(r_xyzd)

def XYZDesign_argparse_wrapper(ai_xyz_args, ai_args_in_txt=""):
  """
  wrapper function of XYZDesign() to call it using the XYZDesign_parser.
  XYZDesign_parser is mostly used for debug and non-regression tests.
  """
  # view the XYZDesign with Tkinter as default action
  tkinter_view = True
  if(ai_xyz_args.sw_simulation_enable or (ai_xyz_args.sw_output_file_basename!='')):
    tkinter_view = False
  # wrapper
  xyzd = XYZDesign_argparse_to_dictionary(ai_xyz_args)
  xyzd['args_in_txt'] = ai_args_in_txt
  xyzd['tkinter_view'] = tkinter_view
  #xyzd['return_type'] = 'int_status'
  r_xyz = XYZDesign(xyzd)
  return(r_xyz)

################################################################
# self test
################################################################

def XYZDesign_self_test():
  """
  This is the non-regression test of XYZDesign.
  """
  test_case_switch = [
    ["Test_A"           , "--param_A 20.0"],
    ["Test B"           , "--param_B 15.0 --param_C 5.0"],
    ["Advanced Test C"  , "--param_A 10.0 --param_B 8.0 --param_C 15.0"]]
  #print("dbg741: len(test_case_switch):", len(test_case_switch))
  XYZDesign_parser = argparse.ArgumentParser(description='Command line interface for the function XYZDesign().')
  XYZDesign_parser = XYZDesign_add_argument(XYZDesign_parser)
  XYZDesign_parser = cnc25d_api.generate_output_file_add_argument(XYZDesign_parser, 1)
  for i in range(len(test_case_switch)):
    l_test_switch = test_case_switch[i][1]
    print("{:2d} test case: '{:s}'\nwith switch: {:s}".format(i, test_case_switch[i][0], l_test_switch))
    l_args = l_test_switch.split()
    #print("dbg414: l_args:", l_args)
    st_args = XYZDesign_parser.parse_args(l_args)
    r_xyzst = XYZDesign_argparse_wrapper(st_args)
  return(r_xyzst)

################################################################
# XYZDesign command line interface
################################################################

def XYZDesign_cli(ai_args=None):
  """ command line interface of XYZDesign.py when it is used in standalone
  """
  # XYZDesign parser
  XYZDesign_parser = argparse.ArgumentParser(description='Command line interface for the function XYZDesign().')
  XYZDesign_parser = XYZDesign_add_argument(XYZDesign_parser)
  XYZDesign_parser = cnc25d_api.generate_output_file_add_argument(XYZDesign_parser, 1)
  # switch for self_test
  XYZDesign_parser.add_argument('--run_test_enable','--rst', action='store_true', default=False, dest='sw_run_self_test',
  help='Generate several corner cases of parameter sets.')
  effective_args = cnc25d_api.get_effective_args(ai_args)
  effective_args_in_txt = "XYZDesign arguments: " + ' '.join(effective_args)
  xyz_args = XYZDesign_parser.parse_args(effective_args)
  print("dbg111: start making XYZDesign")
  if(xyz_args.sw_run_self_test):
    r_xyz = XYZDesign_self_test()
  else:
    r_xyz = XYZDesign_argparse_wrapper(xyz_args, effective_args_in_txt)
  print("dbg999: end of script")
  return(r_xyz)

################################################################
# main
################################################################

if __name__ == "__main__":
  FreeCAD.Console.PrintMessage("XYZDesign.py says hello!\n")
  my_xyz = XYZDesign_cli("--param_A 6.0 --param_B 13.0".split())
  try: # depending on xyz_c['return_type'] it might be or not a freecad_object
    Part.show(my_xyz)
    print("freecad_object returned")
  except:
    pass
    #print("return_type is not a freecad-object")