Author Topic: Creating a new type of MOP?  (Read 130868 times)

Offline pstemari

  • Storm Trooper
  • ***
  • Posts: 146
    • View Profile
Creating a new type of MOP?
« on: February 16, 2013, 21:40:38 pm »
I'd like to create a new MOP subtype to do v-carve style engraving, using the cutter depth to control the width of cut.

I've looked at the demo plugin, and poked around with the object browser, and generally this is what I've figured out:

  • MOPs appear to inherit from CamBam.CAM.MachineOp or CamBam.CAM.MOPFromGeometry.  The latter appears to be the usual case, when a machine op is tied to a particular part of the CAD drawing.
  • The MOP is created with a constructor taking a CamBam.CAD.CADFile and a CamBam.CAD.EntityCollection. Presumably the later is the geometry that the MOP should machine
  • There appears to be three methods that a MOP needs to implement:
    • GenerateToolPaths(CamBam.CAD.CADFile)
    • Paint(CamBam.CAD.CADView, System.Drawing.Graphics, System.Drawing.Pen, System.Drawing.Pen, bool)
    • CamBam.CAM.MOPPocket.PostProcess(CamBam.CAM.MachineOpToGCode)
I'm guessing that GenerateToolPaths is called when you generate toolpaths (d'oh!).  It's not clear if it just updates the MOPs internal data or if it makes changes to the CADFile passed in.

Paint seems like the usual callback.  My guess is that the two pens are for toolpath and cut width, and the bool controls whther to draw the latter.  I'm really taking a stab in the dark on that one.

The PostProcess method appears to generate GCode.  The MachineOpToGCode has a bunch of methods to add g-code to the output, so that seems pretty straight forward.

So, my open questions are:

  • How much did I get right?
  • How do you add a menu item, keyboard accelerator, and toolbar button for the new machine op?
  • What did I miss?

It seems like I should be able to hook in and generate any sort of custom toolpath I like.  Is that about right?
« Last Edit: February 17, 2013, 22:57:33 pm by pstemari »

Offline lloydsp

  • CNC Jedi
  • *****
  • Posts: 9081
    • View Profile
Re: Creating a new type of MOP?
« Reply #1 on: February 16, 2013, 22:21:40 pm »
If you don't want to continuously vary the cut width, then why not make your life simpler, and just use a quick jot of math to figure out what the depth of the cutter point should be?

Depth of cut for any particular width (up to the diameter of the cutter) for a 60-degree (included angle) cutter tip is simply calculated by:

Depth = 0.5(width)*sqrt(2)

For other angles, simply invoking the name of the American Indian Chief Soh Cah Toa will find the solution.

Lloyd
"Pyro for Fun and Profit for More Than Fifty Years"

Offline pstemari

  • Storm Trooper
  • ***
  • Posts: 146
    • View Profile
Re: Creating a new type of MOP?
« Reply #2 on: February 17, 2013, 02:14:00 am »
In case I wasn't clear, I specifically want to continuously vary the cut width.  MeshCAM can produce the desired results, but takes over an hour to engrave since it uses an x-y grid to sculpt the text.  I also tried playing around with using a series of pocketing operations, increasing the tool diameter and depth for each pocket, but the results were suboptimal. You get little shelves in the engraving that are quite noticeable.

Currently I have a Python script than can generate the toolpath, but importing the g-code into CamBam is awkward, and getting the text aligned properly on the part is quite difficult.  Run script, fiddle results with GWE, import script to CamBan, lather rinse repeat.  However, I get excellent results and it only takes a few minutes to execute.

If I write a C# version and load it as a plug-in, then I get a decent integrated solution. 

Offline lloydsp

  • CNC Jedi
  • *****
  • Posts: 9081
    • View Profile
Re: Creating a new type of MOP?
« Reply #3 on: February 17, 2013, 02:41:28 am »
Ok.
Well, you've gotten the rest pretty right, and there are numerous good examples of stand-alone scripts and plugins here on the forum.

We'd love to see what you come up with.

(obviously, you have the math if you can already do this by script!)

One thing... please make your plugin honor all the current post-processor motion clauses, so it will generate code that is "CamBam consistent".

Lloyd
"Pyro for Fun and Profit for More Than Fifty Years"

Offline Dragonfly

  • CNC Jedi
  • *****
  • Posts: 2680
    • View Profile
Re: Creating a new type of MOP?
« Reply #4 on: February 17, 2013, 09:31:00 am »
If I understand correctly, pstemari  wants to make a plugin which will do V-carving, like Vectric VCarve does. If there is a serif type (Times New Roman kind) letters to be engraved then varying the DOC of a V-cutter will produce thinner and thicker cuts to make the serifs and the main beams of a glyph with smooth transition between them.
I have downloaded a free program called F-Engrave which is intended to do that but haven't tried it yet. Anyway such functionality in CB would be an enhancement.
Yesterday I tried to do something similar - to  cut some ornaments with slightly slanted walls, but did it with the standard profile MOP and by manually calculating the with of the cut for a 20 degree (10 degree side angle) bit. My DOC is constant though.

Offline lloydsp

  • CNC Jedi
  • *****
  • Posts: 9081
    • View Profile
Re: Creating a new type of MOP?
« Reply #5 on: February 17, 2013, 12:00:33 pm »
Yes, on the second go-round, he made that perfectly clear.  He also has all the stuff written to do it, and was only asking if he understood the process to create a plugin.

And I think he does... much better than me.

LLoyd
"Pyro for Fun and Profit for More Than Fifty Years"

Offline pstemari

  • Storm Trooper
  • ***
  • Posts: 146
    • View Profile
Re: Creating a new type of MOP?
« Reply #6 on: February 18, 2013, 00:53:07 am »
Well, maybe.   :)  At the moment, I'm just swimming upstream and making a lot of guesses.

f-engrave is pretty much the starting point for this.  I've already hacked on that code a fair amount, but it's exhausted my tolerance for Python's quirky syntax (syntactically significant whitespace?  What's next? Punch cards? I'm getting Cobol and RPG-III flashbacks) and the absence of a decent editor.  Emacs python mode is rather lame.

At this point I understand the algorithm and how f-engrave is processing fonts.  Unfortunately, the latter leaves much to be desired. 

I was able to get the plug-in compiled and loaded.  Setting the target arch to x86 and .net 4.0 were the main hurdles--the demo plugin was a bit stale.


Offline lloydsp

  • CNC Jedi
  • *****
  • Posts: 9081
    • View Profile
Re: Creating a new type of MOP?
« Reply #7 on: February 18, 2013, 01:54:59 am »
(Python) syntactically significant whitespace?  What's next? Punch cards?
-------
Amen!  'Shades blowing a 10000 card WAT5 job because one JCL card near the end had an extra space in it!


Although Python has some neat capabilities, it was put together by a bunch of sadists.

Lloyd
"Pyro for Fun and Profit for More Than Fifty Years"

Offline pstemari

  • Storm Trooper
  • ***
  • Posts: 146
    • View Profile
Re: Creating a new type of MOP?
« Reply #8 on: February 18, 2013, 20:01:10 pm »
In other news, I have a toolpath!!

Not the one I want, but it uses the selected entities and shows up in the display appropriately.

Seems to actually be pretty easy.  I need to fix up the insert location in the menu, figure out where the icon in the document tree is coming from, and set the dirty flag on the document when the MOP gets added, but that's all just housekeeping.

Maybe 40-50 lines of bootstrap junk, including the imports and class names.  Only 15 or so lines of code that actually do anything.  That's really good.  It means I can concentrate on doing interesting stuff and all the housekeeping stuff stays out of the way.

Offline pstemari

  • Storm Trooper
  • ***
  • Posts: 146
    • View Profile
Re: Creating a new type of MOP?
« Reply #9 on: February 18, 2013, 22:10:08 pm »
Well, foo.

None of the virtual Pre/PostProcess methods actually seem to ever get invoked, and no g-code is output at all if my custom MOP is in the set of mops being generated.

Any suggestions as to what I might be missing?

Offline lloydsp

  • CNC Jedi
  • *****
  • Posts: 9081
    • View Profile
Re: Creating a new type of MOP?
« Reply #10 on: February 18, 2013, 23:01:11 pm »
Time for sample code!

We're hoping (unless you sell CAM software for a living) that you'll post your source.  SOME of us are not adept at writing code for this system, and would like to be.  Every example helps.  I've written quite a few 'helper scripts', but never yet a plugin.

That aside, if you post your source here, there are at least four folks besides Andy who'll jump right in with helps.

Lloyd
"Pyro for Fun and Profit for More Than Fifty Years"

Offline pstemari

  • Storm Trooper
  • ***
  • Posts: 146
    • View Profile
Re: Creating a new type of MOP?
« Reply #11 on: February 19, 2013, 15:56:26 pm »
Just boilerplate at this point.  I'd be happy to open source the actual code once there's something usable, but I need to get clearance from TPTB at work.

Code: [Select]
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Drawing;

using CamBam;
using CamBam.CAD;
using CamBam.CAM;
using CamBam.Geom;
using CamBam.UI;
using CamBam.Util;
using CamBam.Values;

namespace VCarve_Plugin
{
    public class VCarve
    {
        private static CamBamUI _ui;

        public VCarve()  {}

        public static void InitPlugin(CamBamUI ui)
        {
            _ui = ui;

            ToolStripMenuItem insertCAM_VcarveCommand = new ToolStripMenuItem();
            insertCAM_VcarveCommand.Text = "V-Carve";
            insertCAM_VcarveCommand.Image = Properties.VCarveResources.VCarveButton;
            insertCAM_VcarveCommand.Click += new EventHandler(InsertCAM_VCarve);

            // this bit is just to control where in the menu the new mop appears.
            for (int i = 0; i < ui.Menus.mnuMachining.DropDownItems.Count; ++i)
            {
                ToolStripItem item = ui.Menus.mnuMachining.DropDownItems[i];
                if (item is ToolStripSeparator)
                {
                    ui.Menus.mnuMachining.DropDownItems.Insert(i, insertCAM_VcarveCommand);
                    insertCAM_VcarveCommand = null;
                    break;
                }
            }
            // Just add it at the end if we didn't find a better place to put it.
            if (insertCAM_VcarveCommand != null)
            {
                ui.Menus.mnuMachining.DropDownItems.Add(insertCAM_VcarveCommand);
            }
        }

        public static void InsertCAM_VCarve(object sender, EventArgs e)
        {
            ICADView view = CamBamUI.MainUI.ActiveView;
            CADFile file = view.CADFile;
            object[] objects = view.SelectedEntities;
            MOPVCarve mop = new MOPVCarve(file, objects);
            view.CADFile.EnsureActivePart(true);
            view.CADFile.ActivePart.MachineOps.Add(mop, /* fireparentevents*/ true);
            // --- this bit doesn't seem to actually work ---
            view.DrawingTree.SelectedMOPs.Clear();
            view.DrawingTree.SelectedMOPs.Add(mop);
            // ---
            view.CADFile.Modified = true;
        }
    }

    public class MOPVCarve : MOPFromGeometry
    {
        public MOPVCarve()  {}

        public MOPVCarve(MOPVCarve src)
            : base(src)
        {}

        public MOPVCarve(CADFile file, object[] objects)
            : base(file, objects)
        {}

        public override string MOPTypeName
        {
            get { return "VCarve"; }
        }

        public override CamBam.CAM.MachineOp Clone()
        {
            return new MOPVCarve(this);
        }

        public override void GenerateToolpaths(CADFile cadFile)
        {
            Toolpaths2 = new ToolpathSequence(this);
            Point3F start = StartPoint.Value;
            // this is just dummy code to stick something into the toolpath
            for (int n = 0; n < PrimitiveIds.Length; ++n)
            {
                Entity entity = cadFile.FindPrimitive(PrimitiveIds[n]);
                if (entity is Polyline)
                {
                    Polyline toolpath = (Polyline)entity;
                    int parentID = 0;
                    if (entity.Parent is Entity)
                    {
                        parentID = ((Entity)entity.Parent).ID;
                    }
                    Toolpaths2.Add(/* depthIndex */ 0,
                                   /* offsetIndex */ 0,
                                   new EntityIdentifier(entity.ID),
                                   parentID,
                                   (Polyline) toolpath.Clone(),
                                   toolpath.Direction,
                                   StartPoint.Value,
                                   /* zoffset */ 0.0);
                }
            }
        }
        // These overrides are just to see if the methods are being called. They aren't :(
        public override bool PreProcess(CamBam.CAM.MachineOpToGCode gcg)
        {
            CamBamUI.MainUI.Messages.Items.Add("base.Preprocess");
            bool result = base.PreProcess(gcg);
            CamBamUI.MainUI.Messages.Items.Add("base.Preprocess returned " + (result ? "true" : "false"));
            return result;
        }

        public override void PostProcess(CamBam.CAM.MachineOpToGCode gcg)
        {
            CamBamUI.MainUI.Messages.Items.Add("base.Preprocess");
            base.PostProcess(gcg);
            CamBamUI.MainUI.Messages.Items.Add("base.Postprocess returned");
        }
    }
}

Offline pstemari

  • Storm Trooper
  • ***
  • Posts: 146
    • View Profile
Re: Creating a new type of MOP?
« Reply #12 on: February 19, 2013, 16:37:45 pm »
Oh, if I override _GenerateToolpathsWorker() instead, I get an asynchronous update, but I need to send some sort of event to the main thread indicating completion.

Offline 10bulls

  • Administrator
  • CNC Jedi
  • *****
  • Posts: 2163
  • Coding Jedi
    • View Profile
    • www.cambam.info
Re: Creating a new type of MOP?
« Reply #13 on: February 19, 2013, 22:02:57 pm »
I'm sorry for leaving you struggle with this one... I've been trying to get a MOP plugin example together but keep getting pulled away on other things.
It sounds like you have done a fantastic job so far (against all odds!  :-[ ).

I will try and get this MOP example finished, but in the mean time, here is a snippit for the _GenerateToolpathsWorker override...(I have not tested this code, so please excuse any errors)

Code: [Select]
        protected override void _GenerateToolpathsWorker()
        {
            try
            {
                ClearToolpaths();
                ToolpathSequence tseq = new ToolpathSequence(this);

                ShapeList shapes = new ShapeList();
                shapes.CloneEntities = true;
                shapes.ApplyTransformations = true;
                shapes.AddEntities(_CADFile, PrimitiveIds);

                if (shapes.Count == 0) return;

                foreach (ShapeListItem item in shapes)
                {
                    if (item.Shape is Polyline)
                    {
                        ToolpathItem tp = new ToolpathItem(0, 0, item.EntityID, -1, (Polyline)item.Shape, RotationDirection.Unknown, Point3F.Undefined, 0);
                        tseq.Add(tp);
                    }
                    else if (item.Shape is CamBam.CAD.Region)
                    {
                        Polyline polouter = ((CamBam.CAD.Region)item.Shape).OuterCurve;
                    ToolpathItem tp = new ToolpathItem(0, 0, item.EntityID, -1, polouter, RotationDirection.Unknown, Point3F.Undefined, 0);
                        tseq.Add(tp);

                        int holenum = 0;
                        foreach (Polyline phole in ((CAD.Region)item.Shape).HoleCurves)
                        {
                            EntityIdentifier holeid = item.EntityID;
                            holeid.SubItem2 = holenum++;

                            ToolpathItem tphole = new ToolpathItem(0, 0, holeid, 0, phole, RotationDirection.Unknown, Point3F.Undefined, 0);
                            tseq.Add(tphole);
                        }
                    }
                }

                // Replace the existing toolpath sequence...
                SetToolpathSequence(tseq);

                Toolpaths2.UseSplitPoint3D = true;
                Toolpaths2.ToolDiameter = ToolDiameter.Cached;

                // Determine the cutting order
                if (this.OptimisationMode.Cached == OptimisationModes.Standard)
                    Toolpaths2.BuildCutOrder(CutOrdering.Cached, MillingDirectionOptions.Mixed, GetDistanceThreshold(), StartPoint);
                else if (this.OptimisationMode.Cached == OptimisationModes.Experimental)
                    Toolpaths2.BuildCutOrderEx_Inside(CutOrdering.Cached, MillingDirectionOptions.Mixed, SpindleDirection.Cached, GetDistanceThreshold(), StartPoint, InsideOutsideOptions.Inside );
                else
                    Toolpaths2.BuildSimpleCutOrder();

                // Used to display the cut width area (optional)
                if (ToolDiameter.Cached != 0)
                    Toolpaths2.CalculateCutWidths(ToolDiameter.Cached, true);

                // Detect Rapid moves (optional...for display only)
                Toolpaths2.DetectRapids(this, GetDistanceThreshold());

                // Must set MachineOpStatus == OK, otherwise g-code is not allowed
                if (MachineOpStatus == MachineOpStatus.Unknown) MachineOpStatus = MachineOpStatus.OK;

            }
            catch (Exception ex)
            {
                MachineOpStatus = MachineOpStatus.Errors;
                ThisApplication.HandleException(ex);
            }
            finally
            {
                // ends and cleans up the worker thread
                _GenerateToolpathsFinal();
            }
        }

Thank you for all your hard work!

Offline pstemari

  • Storm Trooper
  • ***
  • Posts: 146
    • View Profile
Re: Creating a new type of MOP?
« Reply #14 on: February 20, 2013, 15:20:12 pm »
Aha! I shall give MachineOpStatus.OK and _GenerateToolpathsFinal() a try!