Author Topic: Select objects by Z level  (Read 14036 times)

Offline Dragonfly

  • CNC Jedi
  • *****
  • Posts: 2656
    • View Profile
Select objects by Z level
« on: August 06, 2021, 19:05:44 pm »
Hi folks,
For quite a time now I've been receiving orders for machining parts in the form of simple .DXF files accompanied by a 3D model in .stp file format. CamBam, with some small exceptions, reads the .stp files and it is big bonus as I can see the real part on the screen and plan my work strategy. To make it easier for CamBam I normally 'Select by object type' all 3D surfaces and join them into a single 3D surface on a separate layer. This way I can see the 3D model, the wireframe geometry or both.
There are normally various pockets with different depths often one containing another, holes, posts, etc.
Instead of using the 2D .DXF I started by manually selecting elements from the wireframe, then copying to another layer and joining them them to form closed polylines. This way I have the shapes to be machined at their exact depth. Thus helping me with the creation of 2.5D simple MOPs. If the part is not very complex doing it manually does not consume much time.
Then I thought about automating the process for more complex parts where edges are difficult to separate and select all small segments one by one manually is a time consuming work. I wrote a simple and unsophisticated Python script. The latter brought even more pain to the fact that I am not quite familiar with CB API and methods to access and manipulate entities. Why Python? Because I run CamBam for Linux and Python is the only available language there.
The idea is as follows:
I select a single entity from the wireframe and run the script.
The script determines the Z-level (plane) the entity lies on and by iterating through all (visible) entities copies to a new layer all that lie on the same plane (for now I test only both ends by getting entity's extrema.
After a lot of struggle, reading the reference pages and looking into code snippets it started working with very satisfactory results.

But there is a problem I bumped into: Many of the entities in the wireframe are placed at their places by means of transformation matrix.  For example an arc may lie at 10mm above zero but getting its extrema would read 0 if not in 'Identity'  state. This leads to incorrect selections.
I tried using entity clones and 'ApplyTransformation'  method so there positions read correctly but some of them, especially arcs that lie on X/Z or Y/Z plane cause errors. (I actually do not need them but the iteration process goes through them too).

I am starting this thread in the hope that some of you can give me a hand on how to analyze an entity with active transformation matrix.

If successful the script may come handy to those who use .step models in their work. Speaking in the terms of the expensive CAM packages this is a kind of semi-automatic determination of the 'features' of the model.
I can determine whether the matrix is in 'Identity' state or not but could not find a method to read the actual values and determine the Z component.

Here's the code in its present state
Code: [Select]
# Get the Z level of an object
# iterate through all objects and select objects with
# the same Z level within a tolerance

import sys
#from System import Math
#from System import Array
#from System.Collections.Generic import List
from CamBam.UI import CamBamUI
from CamBam.Geom import *
from CamBam.CAD import *
from CamBam.CAM import *
from CamBam.Geom import *

app.log("Begin")

p3max = Point3F()
p3min = Point3F()
p2 = Point2F(1,1)
tmat = Matrix4x4F
sel = list()
Tlrnc = 0.1 #Tolerance, not used currently
selZ = 0
selminZ = 0
selmaxZ = 0

# First we need one and only one entity selected as a guide
if (view.SelectedEntities.Length == 1):
    for ent in view.Selection:
ne = ent.Clone()
        tmat = ne.Transform
mtrx = ne.Transform.ToString()
if (mtrx != 'Identity'):
if (ne.ApplyTransformation(tmat)):
    mtrx = ne.Transform.ToString()
            else:
                app.log("Could not transform selected ID=" + ne.ID.ToString())
                sys.exit(1)

app.log("Object:  "+ne.ID.ToString())
p3min, p3max = ne.GetExtrema(p3min, p3max,False)
app.log("Extrema:  "+p3min.Z.ToString() + " : " +p3max.Z.ToString())
selZ = p3min.Z
selminZ = selZ - Tlrnc
selmaxZ = selZ + Tlrnc
app.log("Level: "+selZ.ToString() )

nlayer = Layer()
nlayer.Name = "SelLayer"
doc.Layers.Add(nlayer)

#Then iterate throu all
    CamBamUI.MainUI.ActiveView.SelectAllVisibleGeometry()
    for ent in CamBamUI.MainUI.ActiveView.SelectedEntities:
        ne = ent.Clone()
        tmat = ne.Transform
        mtrx = ne.Transform.ToString()
        if (mtrx == 'Identity'):
        p3min, p3max = ne.GetExtrema(p3min, p3max,False)
        if ((p3min.Z >= selminZ) and (p3min.Z <= selmaxZ)) :
        if ((p3max.Z >= selminZ) and  (p3max.Z <= selmaxZ)) :
                nlayer.Entities.Add(ne)
else:
                       sys.exit(1)
# Here must be code to look into the matrix values
                       # No success in doing so
else:
    app.log("Error:  Select a single poly.")

« Last Edit: August 06, 2021, 19:29:03 pm by Dragonfly »

Offline Dragonfly

  • CNC Jedi
  • *****
  • Posts: 2656
    • View Profile
Re: Select objects by Z level
« Reply #1 on: August 06, 2021, 19:13:47 pm »
P.S. When selecting entities manually I notice that Eddy's 'Show Extrema' plugin correctly displays the centroid Z level regardless of the active matrix. But I need both ends or a Method to determine that the entity lies on a single flat plane.

I am attaching an example .cb file.
The arcs (marking a hole) in the Red layer are not in 'Identity' state.
« Last Edit: August 06, 2021, 19:30:40 pm by Dragonfly »

Offline EddyCurrent

  • CNC Jedi
  • *****
  • Posts: 5269
  • Made in England
    • View Profile
Re: Select objects by Z level
« Reply #2 on: August 06, 2021, 20:13:55 pm »
If, max.Z - min.Z = 0 then the object is flat.

I don't think the extrema plugin does anything special, here is part of the code (C#) that gets the info. I can't handle Python all I seem to generate are errors  ???

Code: [Select]

// from the Params Class
 public static int decimal_places = 6;
//

Point3F extMin = new Point3F();
Point3F extMax = new Point3F();

if(view.Selection.Count >0)
                {
                    outputString = "";
                    // extrema
                    view.Selection.GetExtrema(ref extMin, ref extMax);
                    extMin = RoundPoint3F(extMin, Params.decimal_places);
                    extMax = RoundPoint3F(extMax, Params.decimal_places);
                    string la = "";
                    // dimensions
                    double width = RoundDouble(Math.Abs(extMax.X - extMin.X),Params.decimal_places);
                    double height = RoundDouble(Math.Abs(extMax.Y - extMin.Y),Params.decimal_places);
                    double depth = RoundDouble(Math.Abs(extMax.Z - extMin.Z),Params.decimal_places);
                    // centroid
                    Point3F mid = RoundPoint3F(view.Selection.MidPoint,Params.decimal_places);
                    // topleft
                    Point2F tl = RoundPoint2F(new Point2F(extMin.X, extMax.Y),Params.decimal_places);
                    // topright
                    Point2F tr = RoundPoint2F(new Point2F(extMax.X, extMax.Y),Params.decimal_places);
                    // bottomleft
                    Point2F bl = RoundPoint2F(new Point2F(extMin.X, extMin.Y),Params.decimal_places);
                    // bottomright
                    Point2F br = RoundPoint2F(new Point2F(extMax.X, extMin.Y),Params.decimal_places);

   // some code here but not required for this example
}

public Point3F RoundPoint3F(Point3F unrounded, int precision)
        {
            Point3F rounded = new Point3F();

            rounded.X = System.Math.Round(unrounded.X, precision, MidpointRounding.AwayFromZero);
            rounded.Y = System.Math.Round(unrounded.Y, precision, MidpointRounding.AwayFromZero);
            rounded.Z = System.Math.Round(unrounded.Z, precision, MidpointRounding.AwayFromZero);

            return rounded;
        }

public Point2F RoundPoint2F(Point2F unrounded, int precision)
        {
            Point2F rounded = new Point2F();

            rounded.X = System.Math.Round(unrounded.X, precision, MidpointRounding.AwayFromZero);
            rounded.Y = System.Math.Round(unrounded.Y, precision, MidpointRounding.AwayFromZero);

            return rounded;
        }

 public double RoundDouble(double unrounded, int precision)
        {
            double rounded = 0.0;

            rounded = System.Math.Round(unrounded, precision, MidpointRounding.AwayFromZero);
           
            return rounded;
        }

« Last Edit: August 06, 2021, 20:24:27 pm by EddyCurrent »
Filmed in Supermarionation

Offline Dragonfly

  • CNC Jedi
  • *****
  • Posts: 2656
    • View Profile
Re: Select objects by Z level
« Reply #3 on: August 07, 2021, 07:58:17 am »
Quote
If, max.Z - min.Z = 0 then the object is flat.
Thanks, Eddy. Didn't guess about this method.

Offline Dragonfly

  • CNC Jedi
  • *****
  • Posts: 2656
    • View Profile
Re: Select objects by Z level
« Reply #4 on: August 07, 2021, 09:42:12 am »
Some notes on Python behavior in Windows and Linux
1. sys.exit() in Linux is not functional. Execution of code continues after it. In Windows the script exits with the given exit code.
2. In Linux script editing/execution window is very basic and buggy. No syntax highlighting, indentation is not displayed correctly, text cursor sometimes is not at the actual point of writing on the screen. My guess is it uses a very simple Linux text terminal window.

Offline Dragonfly

  • CNC Jedi
  • *****
  • Posts: 2656
    • View Profile
Re: Select objects by Z level
« Reply #5 on: August 07, 2021, 10:22:24 am »
And here is a real mystery.
This is a screenshot of the example file.
The selected arc is transforned and lies on a vertical plane. Should be skipped by the script.
On the picture you can see what info CamBam displays, what values the script reads and what 'Show Extrema' plugin displays.
The 'Show Extrema' plugin actually shows the real correct values.
I am using the same methods in Python but returned values are not the same.
Could it be the program language used?

Note: 'Apply transformations' from CamBam on the selected arc does nothing. No reset to 'Identity'.
« Last Edit: August 07, 2021, 10:25:22 am by Dragonfly »

Offline Dragonfly

  • CNC Jedi
  • *****
  • Posts: 2656
    • View Profile
Positive development
« Reply #6 on: August 07, 2021, 12:32:57 pm »
There is a positive development after some head banging :)
Looking into Eddy's code snippet I noticed that he uses the following way to refer to the selection of entites:
Code: [Select]
if(view.Selection.Count >0)which in Python becomes
Code: [Select]
for ent in view.Selection:and in my script I was using:
Code: [Select]
for ent in CamBamUI.MainUI.ActiveView.SelectedEntities:
I have no explanation but referring with view.Selection returns different values - correct ones for the examined entity.
The modified code became shorter and seems to work OK.
Code: [Select]
# Select-by-Z-Level v.0.2
# Get the Z level of an object
# iterate through all objects and select objects with
# the same Z level within a tolerance

import sys
from System import Math
#from System import Array
#from System.Collections.Generic import List
from CamBam.UI import CamBamUI
from CamBam.Geom import *
from CamBam.CAD import *
from CamBam.CAM import *
from CamBam.Geom import *

app.log("Begin")

p3max = Point3F(1,1,1)
p3min = Point3F(2,2,2)
p3mid = Point3F(3,3,3)
p2 = Point2F(1,1)
tmat = Matrix4x4F
sel = list()
Tlrnc = 0.1 #Tolerance, not used currently
selZ = 0
doall = 1

def IsFlat():
    q = Math.Abs(p3max.Z - p3min.Z)
#    app.log("p3max:"+p3max.ToString()+" p3min:"+p3min.ToString())
    if (q == 0):
        return 1
    else:
        return 0

# First we need one and only one entity selected as a guide
#view.Selection.GetExtrema(ref extMin, ref extMax);

if (view.SelectedEntities.Length == 1):
    p3min, p3max = view.Selection.GetExtrema(p3min, p3max)
    p3mid = view.Selection.MidPoint
    app.log("Min:"+p3min.ToString())
    app.log("Max:"+p3max.ToString())
    app.log("Mid:"+p3mid.ToString())
    if (IsFlat() == 1):
        app.log("Is flat")
        selZ = p3mid.Z
        app.log("Level:"+p3mid.Z.ToString())

        if (doall == 1):
            nlayer = Layer()
            nlayer.Name = "SelLayer"
            doc.Layers.Add(nlayer)
            CamBamUI.MainUI.ActiveView.SelectAllVisibleGeometry()

#            for ent in CamBamUI.MainUI.ActiveView.SelectedEntities:    -> returns wrong values
            for ent in view.Selection:
                ne = ent.Clone()
                tmat = ne.Transform
                mtrx = ne.Transform.ToString()
                p3min, p3max = ne.GetExtrema(p3min, p3max,False)
                p3mid = ne.GetCentroid()
                if (IsFlat() == 1):
                    if (p3mid.Z == selZ):
                       nlayer.Entities.Add(ne)
#            CamBamUI.MainUI.ActiveView.Deselect() ???
               
    else:
        app.log("Selected reference not flat")
else:
    app.log("Error:  Select a single poly.")

I need clues for:
1. How to deselect all from program code.
2. How to create layers with automatically assigned names. Currently if run multiple times the script creates more than one layer wit one and the same name and CamBam does not complain but it makes the work untidy.
« Last Edit: August 07, 2021, 12:36:11 pm by Dragonfly »

Offline EddyCurrent

  • CNC Jedi
  • *****
  • Posts: 5269
  • Made in England
    • View Profile
Re: Select objects by Z level
« Reply #7 on: August 07, 2021, 13:52:31 pm »
view is defined in the Show Extrema plugin as;

static ICADView view = CamBamUI.MainUI.ActiveView;

so it becomes, CamBamUI.MainUI.ActiveView.Selection.Count

if(view.Selection.Count >0)
// has at least one entity been selected

// then work through each selected entity one at a time
foreach (Entity ent in view.Selection)
            {
            }


This is the code I use to make a unique layer name
and this is how it's called,

ICADView view = CamBamUI.MainUI.ActiveView;
CADFile myfile = view.CADFile;
public static Layer NewLayer;

MakeNewLayer("PolylineSmooth ", ref NewLayer);

// make it the active layer
myfile.SetActiveLayer(NewLayer.Name);


Code: [Select]
// Make a New Layer, return unique name if requested layer exists
        public static string MakeNewLayer(string DesiredName, ref Layer NewLayer)
        {
            string ActualName = DesiredName;
            int Index = 1;

            while (CamBamUI.MainUI.ActiveView.CADFile.HasLayer(ActualName))
            {
                ActualName = DesiredName + "(" + Index.ToString() + ")";
                Index++;
            }

            NewLayer = CamBamUI.MainUI.ActiveView.CADFile.CreateLayer(ActualName);
            return ActualName;
        }

« Last Edit: August 07, 2021, 14:15:37 pm by EddyCurrent »
Filmed in Supermarionation

Offline dh42

  • Administrator
  • CNC Jedi
  • *****
  • Posts: 7422
    • View Profile
    • Cambam V1.0 French Doc
Re: Select objects by Z level
« Reply #8 on: August 07, 2021, 19:40:58 pm »
Hello

Quote
I tried using entity clones and 'ApplyTransformation'  method so there positions read correctly but some of them, especially arcs that lie on X/Z or Y/Z plane cause errors. (I actually do not need them but the iteration process goes through them too).

Yes, I falls on the same problem with an imported STEP file where I want to "flatten" the outlines to do a profile shape to cut in 2D ; if there is arcs on XZ or YZ plane, apply transformation on an arc do nothing ; if the arc is converted to polyline,  apply transformation return a bad line ...

Currently I use the following workflow when an arc is on XZ or YZ plane > Convert arc to polyline > polyline remove arcs > apply transformation.

remember that CB can't use arcs in another plane than XY even if it can display them with a transformation matrix ... if you try to use an arc (or an arc into a polyline) with an angle in a MOP you always will get bad toolpath even with the engrave MOP.

If an arc is not on XY plane, it must be transformed to a succussion of straight lines with remove arcs before any use.

edit: When importing a STEP, if an arc is on XY, XZ or YZ plane it is imported as arc (with transformation if on XZ, YZ plane), if the arc is not on one of this planes, it is imported as spline (without transformation) and the spline can be directly converted to polyline, the resulting polyline contain only straight segments.

++
David
« Last Edit: August 07, 2021, 20:13:47 pm by dh42 »

Offline Dragonfly

  • CNC Jedi
  • *****
  • Posts: 2656
    • View Profile
Re: Select objects by Z level
« Reply #9 on: August 08, 2021, 09:12:29 am »
David,
I have the same observations from practical experience. Arcs which are not on X/Y plane do not accept 'Apply transformation'. In CamBam the command simply does nothing, in a script it returns 'False'. When converted to poly the command is executed but the arc (in the form of a poly with bulge)  is rotated to lie on the X/Y plane.
I really hope there will be some more development in CB .step file handling as 3D solid design has become a norm now. (In that respect many young engineers who had never seen an actual milling machine often design parts that a practically impossible to mill with a physical tool. :)  )
Perhaps it depends from what 3D CAD program the .step has been exported. I am getting .step files exported from SolidWorks and sometimes I reexport them from FreeCAD and arcs are always arcs, not splines.

My current script successfully determines whether an entity is flat on the X/Y plane and skips the rest. And when all placed on the same Z-level are selected and copied to a new layer it is easy to apply 'Join' and get closed shapes.
« Last Edit: August 08, 2021, 09:16:11 am by Dragonfly »

Offline dh42

  • Administrator
  • CNC Jedi
  • *****
  • Posts: 7422
    • View Profile
    • Cambam V1.0 French Doc
Re: Select objects by Z level
« Reply #10 on: August 08, 2021, 17:48:40 pm »
Hello

Quote
I am getting .step files exported from SolidWorks and sometimes I reexport them from FreeCAD and arcs are always arcs, not splines.

I also export from Solidworks as AP203 STEP format ... and I get splines when arcs are not // to a plane ... I do not see any setting in the export panel to choose other output for arcs, or to force arcs to be converted to spline all the time ... an example of a simple part with a hole on a inclined area ... of course in this case it's not really an arc but a curve so it can't be generated as arc.

++
David

Offline EddyCurrent

  • CNC Jedi
  • *****
  • Posts: 5269
  • Made in England
    • View Profile
Re: Select objects by Z level
« Reply #11 on: August 08, 2021, 19:09:48 pm »
If the idea is to find 'flat' objects at a particular Z level, why does it matter about arcs in a different plane ?
Filmed in Supermarionation

Offline Dragonfly

  • CNC Jedi
  • *****
  • Posts: 2656
    • View Profile
Help with Python needed
« Reply #12 on: August 08, 2024, 19:53:30 pm »
Hi folks,
as some of you know I made and try to enhance a Python script which selects entities which lie on single level of he X/Y plane in complex 3D objects obtained from .STEP files. Much needed when defining separate levels for milling in 2.5D work.
Although it works for me and is incomparable to manual picking lines and arcs in a jungle of similar elements there is a problem I can not solve with my limited knowledge of CB internals.
Many objects, predominantly arcs and circles, are placed in their respective place with the help of a Transformation matrix. For example, if I want to select geometry entities at Z level = 0, there are circles on the side walls of the part and at 90 degrees to the selected X/Y plane. But they get selected because GetExtrema method returns equal Z values - the circle is dynamically placed where it lies by using the matrix.
I can read the matrix and determine whether it is "Identity", or has non-zero values. But my attempt to apply the Transformation to an entity clone fails. The clone Transformation Matrix stays the same as the original.
How the entity.ApplyTransformation(mx) method is executed?
Either that, or use some other method to get the real position of an entity. But I cannot guess how.

This is possible, because the 'Show Extrema' plugin by Eddy correctly displays the 3D center coordinates of a circle even if it lies on the X/Z plane by using a Transformation matrix.

Any help is much appreciated.
« Last Edit: August 08, 2024, 19:56:33 pm by Dragonfly »

Offline dh42

  • Administrator
  • CNC Jedi
  • *****
  • Posts: 7422
    • View Profile
    • Cambam V1.0 French Doc
Re: Help with Python needed
« Reply #13 on: August 08, 2024, 23:51:46 pm »
Hello

If an arc or circle is not on the XY plane, apply transformation has no effect.

If the arc/circle is converted to a polyline, apply transformation works but .... the line is distorted.

If you apply a "remove arcs" on the polyline, apply transformation works and the line is not distorted.

To reject arc/circles that are not on the XY plane and that can't be transformed, you can use the ApplyTransformation() test on the entity ; if it return "false", that means that the entity can't by transformed and certainly that it is not on the right plane.

Example with this file, all shapes except arc(11) and circle(12) can be transformed (it is only a test, it do not apply the transformation)

The VB code (sorry, not a python coder)

Code: [Select]
' New CamBam VBScript

sub main
    dim ent as Entity
   
        for each ent in view.SelectedEntities
       
        app.log (ent.id & "  " & ent.ApplyTransformation)
       
    next ent
   
end sub

++
David

Offline Dragonfly

  • CNC Jedi
  • *****
  • Posts: 2656
    • View Profile
Re: Help with Python needed
« Reply #14 on: August 09, 2024, 13:33:57 pm »
David,
there is a thread on this subject created by myself but I've forgotten
https://cambamcnc.com/forum/index.php?topic=9255.msg72345#msg72345
Would you please merge this one with the old?

Also, I've discovered that if an entity is selected ON SCREEN then code snippet I use for testing returns the true values with
Code: [Select]
p3min, p3max = view.Selection.GetExtrema(p3min, p3max).
That is because the CB UI has interpreted the T_Matrix and placed the object where it should be. And this explains why Eddy's plugin shows correct data.
BUT while iterating through a selection each selected item returns its original position without transformations. It contains a T_Matrix different from 'Identity' but of not much help for my purpose. And if I test True/False with ApplyTransformation the code actually changes the item if it is transformable. But they in my case come out scaled and moved from their original places. Seems like the matrix transformation is applied twice.
Anyway, I came to the conclusion that the best approach is to 'walk' through entities on-screen, selecting a single one on each iteration (make it selected on screen). But I don't know whether it is possible and how to do it due to lack of knowledge on CB internals. But if an item can be selected with a mouse click, then it should be possible in a script.
« Last Edit: August 09, 2024, 13:35:49 pm by Dragonfly »