Hello everyone!
I use PM macro language for a while, mostly picking the commands from command window in echo mode.
Currently I'm trying to write a macro for iterating some selected surfaces of the model, but I found that my knowledge isn't sufficient for the task.
I need to use FOREACH loop, but I can't obtain a list of selected by user surfaces.
What type should the list be, entity?
No matter what to use, list, levels, arrays - I haven't succeed yet to iterate it.
Any suggestions appreciated.
//user picks needed surfaces manually, then CREATE LEVEL 'surfs' MODELCOMPSET EDIT LEVEL 'surfs' ACQUIRE SELECTED FOREACH surf IN Level 'surfs' { PRINT $surf.Name //etc }
Solved! Go to Solution.
Solved by 5axes. Go to Solution.
CREATE LEVEL 'surfs' MODELCOMPSET activate LEVEL 'surfs' MACRO PAUSE "Seleccione Superficies!"+crlf+""+crlf+""+crlf+""+crlf+" Aprete Reanudar "+crlf+""+crlf+"Cuendo este listo!" // a message to you EDIT LEVEL "surfs" ACQUIRE SELECTED EDIT LEVEL "surfs" SELECT SURFACE message info "Proceso Terminado"
try this
Thank you for your answer, rob_carri,
but I have no problem with adding surfaces into a set.
The problem that I want to solve, is to select all surfaces from the existing set (or from array, or from list, whatever)
one by one automatically
and conduct some actions to them, say print names of all surfaces in the set.
As far as I know, I need to use FOREACH loop.
There isn't a direct way to FOREACH over selected surfaces.
You could try opening a tracefile and then doing PRINT SELSURFACE
TRACEFILE OPEN "sel_surfs.txt" PRINT SELSURFACE TRACEFILE CLOSE STRING LIST surfs = {} FILE OPEN "sel_surs.txt" FOR READ AS "input" FILE READ $surfs FROM "input" FILE CLOSE "input" // Get read of the first element as it is the count int n = remove_first(surfs) EDIT MODEL ALL DESELECT ALL FOREACH surf IN surfs { EDIT MODEL ALL SELECT $surf //... EDIT MODEL ALL DESELECT $surf }
Thank you very much for your respond, I'll try your code.
Why is it impossible to iterate surfaces directly?
In PM parameters list there is "Model components list", but I don't understand how to use this syntax properly:
If you will make a list over your models in pmill, you can use this.
STRING $Model_name = ""
$Model_name = INPUT ENTITY MODEL "Choose your model"
Thanks, your post is useful, but it doesn't solve the problem.
Your code helps to select a model, whether I want to select not a model, but certain surfaces in it, and not at once, but one by one.
urizen, your suggestion is almost working,
it returns a file containing needed surfaces' names and try to iterate it, but throws out an error:
PM recognizes the name, but can't process it. Why is "Model Component _it's name_ doesn't exist???
On the right side of debugger it can be seen, that the list of surfaces is successfully generated, and iterable surf successfully takes the first instance from the list, too.
It should work, but it doesn't.
Try:
TRACEFILE OPEN "sel_surfs.txt" PRINT SELSURFACE TRACEFILE CLOSE STRING LIST surfs = {} FILE OPEN "sel_surs.txt" FOR READ AS "input" FILE READ $surfs FROM "input" FILE CLOSE "input" // Get read of the first element as it is the count int n = remove_first(surfs) EDIT MODEL ALL DESELECT ALL FOREACH surf IN surfs { EDIT MODEL ALL SELECT $surfs // edit from $surf to $surfs //... EDIT MODEL ALL DESELECT $surfs // edit from $surf to $surfs }
can you use this?
// MAKE LEVEL
string name_level = ""
string prompt = "Enter a name for level"
$name_level = input $prompt
// SELECT MODEL
STRING $Model_name = ""
$Model_name = INPUT ENTITY MODEL "Choose your model"
// MAKE LEVEL AND RENAME
CREATE LEVEL ; LEVEL
RENAME Level # $name_level
// PAUSE TO SELECT SURFACES
MACRO PAUSE "SELECT SURFACES"
EDIT LEVEL $name_level ACQUIRE SELECTED
EDIT LEVEL $name_level SELECT ALL
EDIT MODEL $Model_name REVERSE
thanks a lot @urizenYHS3W for this solution. I've never thought to use this trick !! Could be usefull..
Hereafter the code with some modifications to make it usable :
INT Pos=0 INT Pos2=0 INT Lgth=0 STRING Cmd='' STRING ActSurf='' INT count = 0 FOREACH m in folder('model') { $count = $count + number_selected(m.Components) } // MESSAGE INFO "There are " + $count + " surfaces selected." if $count > 0 { ECHO OFF DCPDEBUG UNTRACE COMMAND ACCEPT //Important to supress useless line in the Tracefile Log TRACEFILE OPEN "c:\Temp\sel_surfs.txt" PRINT SELSURFACE TRACEFILE CLOSE STRING LIST surfs = {} FILE OPEN "c:\Temp\sel_surfs.txt" FOR READ AS "input" FILE READ $surfs FROM "input" FILE CLOSE "input" // Get read of the first element as it is the count int n = remove_first(surfs) FOREACH surf IN surfs { $Pos=position($surf,"Process Command") $Pos2=position($surf,"There are") $Lgth=length($surf) IF $Pos >= 0 OR $Pos2 >= 0 OR $Lgth==1 { PRINT "Error command $surf" } ELSE { $ActSurf=RTRIM($surf) // Supress the CR at the end of the line from $surf to $surfs EDIT MODEL ALL DESELECT ALL EDIT MODEL ALL SELECT $ActSurf // edit from $surf to $surfs // Do wath you want } } } ELSE { MESSAGE WARN "No Surface selected" }
This MACRO looks very close to the solution I've been looking for! Thanks to everyone so far.
I added
//Do anything PRINT $ActSurf
and it's printing out a nice list of the selected surfaces names.
Now I'd like to programmatically apply a Surface Finish Path to each surface, with minor edits to each.
How do I begin this step?
Good Afternoon 5axes,
This macro really interests me, probably because I don't understand the function. I ran the macro in 2019 and saw what it did. Why is iterating the surfaces useful?
Hello,
This sample code is not really useful. It's just an example to illustrate the use of this tip to get the list of selected surfaces.
Hereafter a sample code where the use of PRINT SELSURFACE have more sens .
FUNCTION main() {
STRING LIST surfs = {}
CALL GetFlatFacesName($surfs)
IF SIZE($surfs) == 0 {
MESSAGE WARN 'Flat surface cannot be found !'
RETURN
}
REAL LIST Zlevel = {}
CALL GetZLevelList($surfs, $Zlevel)
STRING $msg = 'Flat surfaces Z value :' + crlf + JOIN(Zlevel, crlf)
MESSAGE INFO $msg
}
// GET Zhight via block calculation
FUNCTION GetZLevelList(STRING LIST surfs, OUTPUT REAL LIST ZMin) {
$ZMin = {}
UNDRAW BLOCK
EDIT BLOCKTYPE BOX
EDIT BLOCK COORDINATE WORKPLANE
EDIT BLOCK ALL UNLOCK
EDIT BLOCK TOLERANCE '0.01'
EDIT BLOCK RESETLIMIT '0'
EDIT BLOCK LIMITTYPE MODEL
STRING LIST pros = {}
INT Ret = 0
STRING face = ''
GRAPHICS LOCK
FOREACH s IN $surfs {
EDIT MODEL ALL DESELECT ALL
$face = RTRIM($s)
EDIT MODEL ALL SELECT $face
EDIT BLOCK RESET
$Ret = ADD_LAST($ZMin, ROUND($Block.Limits.ZMin,3))
}
GRAPHICS UNLOCK
EDIT MODEL ALL DESELECT ALL
// Remove from the list same value and order in the reverse order the values
$Ret = REMOVE_DUPLICATES($ZMin)
$ZMin = REVERSE(SORT($ZMin))
}
// Gey the name of the flat surfaces
FUNCTION GetFlatFacesName(OUTPUT STRING LIST Surf) {
// Temporary file path
STRING $TraceFilePath = MACRO_PATH(false) + "\Surflist.txt"
$Surf = {}
EDIT MODEL ALL DESELECT ALL
// Select flat surface
EDIT MODEL ALL SELECT FLAT
ECHO OFF DCPDEBUG UNTRACE COMMAND ACCEPT
TRACEFILE OPEN $TraceFilePath
PRINT SELSURFACE
TRACEFILE CLOSE
ECHO ON DCPDEBUG TRACE COMMAND ACCEPT
FILE OPEN $TraceFilePath FOR READ AS Input
FILE READ $Surf FROM Input
FILE CLOSE Input
DELETE FILE $TraceFilePath
INT Ret = REMOVE_FIRST($Surf)
}
In this code the Function GetFlatFacesName get the list of Flat surface of the model.
Then the Z Value of each surface are calculated via a BLOCK calculation as powermill doesn't offer any function to get a surface dimension.
@Beta_Librae My team and I were looking to use the "iterate face" code to essentially 3+2 machine the faces of a large low-polyhedral sculpture. In an ideal world this cuts down on machine time by about 80%-90%.
In the end we ran into issues with the PowerMill Robot Plugin and the huge number of toolpaths produced by the code. So we had to scrap this approach and just ball nose the whole thing with a 1mm step-over. Takes forever, but looks good.
Below is the code that got close the solution. So many more things we would have liked to do with it. Could potentially be useful in some specific industry of machining generative low-poly designs.
Huge shout out to @5axes, who was extremely helpful getting this code this far. Sorry it never saw glory.
// -- // // PolyFace_NAPA - METRIC // Iterates through selected surfaces and automatically generates a surface finish toolpath normal to each. // -- // // New American Public Art - Kempelen's Owls [newamericanpublicart.com/kempelens-owls] // This code is provided under a CC-BY-SA 4.0 licenses [https://creativecommons.org/licenses/by-sa/4.0/] //// Authors : //// New American Public Art //// 5axes [https://forums.autodesk.com/t5/user/viewprofilepage/user-id/3969139)] //// Autodesk BUILD Space // -- // INT ErrorCheck1=0 INT ErrorCheck2=0 INT Lgth=0 STRING ActSurf='' INT count = 0 REAL iVal = 0 REAL jVal = 0 REAL kVal = 0 FOREACH m in folder('model') { $count = $count + number_selected(m.Components) } MESSAGE INFO "There are " + $count + " surfaces selected." if $count > 0 { ECHO OFF DCPDEBUG UNTRACE COMMAND ACCEPT //Important to suppress useless line in the Tracefile Log TRACEFILE OPEN "surfs.txt" PRINT SELSURFACE TRACEFILE CLOSE STRING LIST surfs = {} FILE OPEN "surfs.txt" FOR READ AS "input" FILE READ $surfs FROM "input" FILE CLOSE "input" // Get rid of the first element as it is the count int n = remove_first(surfs) FOREACH surf IN surfs { $ErrorCheck1=position($surf,"Process Command") // look for error sign $ErrorCheck2=position($surf,"There are") // look for error sign $Lgth=length($surf) IF $ErrorCheck1 >= 0 OR $ErrorCheck2 >= 0 OR $Lgth==1 { PRINT "Error command $surf" } ELSE { $ActSurf=RTRIM($surf) // Suppress the CR at the end of the line EDIT MODEL ALL DESELECT ALL EDIT MODEL ALL SELECT $ActSurf PRINT $ActSurf //-- run dummy toolpath to get IJK of $ActSurf --// EDIT BLOCK RESETLIMIT "1" EDIT BLOCK RESET ACTIVATE TOOL "tempTool" //-- tempTool should be as small and long as your program allows IMPORT TEMPLATE ENTITY TOOLPATH TMPLTSELECTORGUI "Finishing/Profile-Finishing.ptf" EDIT PAR 'StockEngagement.MachineStockOnly' 0 EDIT PAR 'SurfaceSide' 'outside' EDIT PAR 'RadialOffset' "-1.27" EDIT PAR 'ProfileBasePosition' 'automatic' EDIT PAR 'JoinTolerance' "0.00" EDIT FTOLERANCE "0.0508" EDIT PAR 'Thickness' "0" EDIT TOOLAXIS TYPE LEADLEAN EDIT TOOLAXIS LEAD "0" EDIT TOOLAXIS LEAN "0" EDIT TOOLPATH ; CALCULATE FORM ACCEPT SFPatternProf INT nb_point=segment_point_count( entity('Toolpath',''),0) IF $nb_point == 0 { MESSAGE ERROR "NO toolpath calculated" } ELSE { $nb_point=$nb_point/2 object Analyse= segment_get_point( entity('Toolpath',''), 0, $nb_point) REAL i = round($Analyse.ToolAxis[0],5) REAL j = round($Analyse.ToolAxis[1],5) REAL k = round($Analyse.ToolAxis[2],5) //save these for later $iVal = $i $jVal = $j $kVal = $k } DELETE TOOLPATH ; //Reset some stuff ACTIVATE TOOL "1/2 end mill" //Run the real toolpath FORM STRATEGYSELECTOR STRATEGYSELECTOR STRATEGY "Finishing/Surface-Finishing.ptf" NEW IMPORT TEMPLATE ENTITY TOOLPATH TMPLTSELECTORGUI "Finishing/Surface-Finishing.ptf" //-- Workplane ACTIVATE WORKPLANE "objectMod" //-- Block //// no changes, should be triangles, Global Transform //-- Tool //// no changes, should be active tool as set above //-- Machine Tool EDIT PAR 'ModelLocation' "ABBLoc" //-- Limit //// no changes //-- Stock Engagement EDIT TPPAGE SWStockEngage EDIT PAR 'StockEngagement.MachineStockOnly' 1 EDIT PAR 'StockEngagement.ThresholdThickness' "0.0508" EDIT PAR 'StockEngagement.MinimumLengthRemoved' "4.7625" EDIT PAR 'StockModelState.StockModel' "stockMod" EDIT STOCKMODEL 'stockMod' ACTIVATE INDEXED_STATE '1' //-- Surface finishing EDIT PAR 'SurfaceSide' 'outside' EDIT PAR 'ProjectionSurfaceUnits' 'distance' EDIT PAR 'Degouge.Tolerance' "0.254" EDIT FTOLERANCE "0.0508" ////--- Thickness is the big difference between PolySurf.macs ---///// ////--- this leaves a little stock to be machined in a final pass --// EDIT PAR 'Thickness' "1.27" EDIT PAR 'RadialDepthOfCut.UserDefined' '1' Edit Par 'Stepover' "6.35" //-- Surface finishing -- Pattern EDIT TPPAGE SWProjectPatSurf EDIT PAR 'ProjectionPatternDirection' 'v' EDIT PAR 'Spiral' 0 EDIT PAR 'PatternStyle' 'two_way' EDIT SURFPROJ STARTCORNER MINU_MINV EDIT FINPROJ ORDER NONE //-- Automatic Verificaiton EDIT TPPAGE SWAutoVerifBasic EDIT PAR 'Clearance.Head' "609.6" EDIT PAR 'CollisionCheck' '1' EDIT PAR 'Clearance.Holder' "0.0" EDIT PAR 'Clearance.Shank' "0.0" //-- Point Distribution //// no changes //-- Tool axis EDIT TPPAGE SWToolAxis EDIT TOOLAXIS TYPE DIRECTION EDIT TOOLAXIS DIRI $iVal EDIT TOOLAXIS DIRJ $jVal EDIT TOOLAXIS DIRK $kVal EDIT COLLISIONAVOIDANCE SET ON EDIT TOOLAXIS SMOOTHING ON //-- Tool axis -- Collision Avoidance EDIT TPPAGE SWToolAxCAvd EDIT PAR 'CollisionAvoidance.Method' 'lead_then_lean' EDIT COLLISIONAVOIDANCE HOLDER_CLEARANCE "1.27" EDIT COLLISIONAVOIDANCE SHANK_CLEARANCE "1.27" EDIT PAR 'ToolAxisSmoother.Distance' "38.1" //-- Tool axis -- Smoothing EDIT TPPAGE SWToolAxSmth EDIT PAR 'ToolAxisSmoother.ElevationSmoothing' 'smoothed' EDIT PAR 'ToolAxisSmoother.Angle' "5.0" EDIT PAR 'ToolAxisSmoother.AzimuthSmoothing' 'smoothed' EDIT PAR 'ToolAxisSmoother.AzimuthAngle' "5.0" EDIT PAR 'ToolAxisSmoother.Distance' "38.1" //-- Machine axis controll //// no changes //-- Rapid Moves EDIT TPPAGE SWToolRapidMv EDIT TOOLPATH SAFEAREA TYPE PLANE EDIT TOOLPATH SAFEAREA WORKPLANE "objectMod" EDIT TOOLPATH SAFEAREA DIRECTION I "0" EDIT TOOLPATH SAFEAREA DIRECTION J "0" EDIT TOOLPATH SAFEAREA DIRECTION K "1" EDIT TOOLPATH SAFEAREA MEASURE_FROM BLOCK //-- These need to take into consideration the roughing geometery from the last paths EDIT PAR 'Rapid.CalculateDimensions.RapidClearance' "25.4" EDIT PAR 'Rapid.CalculateDimensions.PlungeClearance' "7.62" EDIT TOOLPATH SAFEAREA CALCULATE_DIMENSIONS // Rapid moves -- Moves and clearances EDIT TPPAGE SWToolRapidMVClear EDIT TOOLPATH LEADS LINK MOVE_DIR NORMAL EDIT TOOLPATH LEADS LINK ARCFIT Y EDIT TOOLPATH LEADS LINK ARCFIT_RAD "19.05" EDIT TOOLPATH LEADS SKIMDIST "6.35" EDIT TOOLPATH LEADS RADIAL_CLEARANCE 6.35 //-- Leads and Links EDIT TPPAGE SWLeadsLinks EDIT TPPAGE SWLeadIn EDIT TOOLPATH LEADS LEADIN RAMP FORM PMLLEADINRAMP EDIT TOOLPATH LEADS RAMPPAGE LEADINRAMPOPT1 EDIT TOOLPATH LEADS LEADIN RAMPOPT FOLLOW CIRCLE EDIT TOOLPATH LEADS LEADIN2 POCKET LEADINRAMP ACCEPT EDIT TPPAGE SWLeadOut EDIT TOOLPATH LEADS LEADOUT RAMP FORM PMLLEADOUTRAMP EDIT TOOLPATH LEADS RAMPPAGE LEADOUTRAMPOPT1 EDIT TOOLPATH LEADS LEADOUT RAMPOPT FOLLOW CIRCLE EDIT TOOLPATH LEADS LEADIN RAMPOPT DIAMETER ".5" //--TDU EDIT TOOLPATH LEADS LEADIN RAMPOPT ZIGANGLE "30.0" EDIT TOOLPATH LEADS LEADIN RAMPOPT HEIGHT_TYPE INCR EDIT TOOLPATH LEADS LEADIN RAMPOPT HEIGHT_INCREMENT "7.62" //-- increase this if you are getting rapid collisions during retract moves LEADINRAMP ACCEPT EDIT TOOLPATH LEADS LEADOUT2 POCKET EDIT TOOLPATH LEADS LEADOUT COPY LEADOUTRAMP ACCEPT EDIT TPPAGE SWLink EDIT PAR 'Connections.Link[0].Type' 'straight' EDIT PAR 'Connections.Link[0].ApplyConstraints' '1' EDIT PAR 'Toolpath.Connections.Link.First.Constraint[0].Azimuth.Function' 'less_than' EDIT PAR 'Toolpath.Connections.Link.First.Constraint[0].Azimuth.Value' "0.254" EDIT PAR 'Connections.Link[1].Type' 'circular_arc' EDIT TPPAGE SWSEPtSPoint EDIT TOOLPATH START TYPE POINT_SAFE EDIT TPPAGE SWSEPtEPoint EDIT TOOLPATH END TYPE ABSOLUTE EDIT PAR 'EndPoint.Motion' 'reset_machine_tool' //-- this solves many retract and reconfigure errors //-- these numbers are the xzy cords of your specific center safe EDIT TOOLPATH END POSITION X "154.0" EDIT TOOLPATH END POSITION Y "270" EDIT TOOLPATH END POSITION Z "20" EDIT TOOLPATH ; CALCULATE // need some error checking in here FORM ACCEPT SFProjectionSurfM } } ELSE { MESSAGE INFO " DONE! " + $count + " surfaces completed." //MESSAGE WARN "No surfaces found" } // - follow this up with an "Optimised Constant Z Finising" // closed offsets, smoothing, centreline // tool axis = lead/lean // leads and links = ramp, ramp, straight, safe
@5axes @dan Thanks for the insight guys. I was really hoping that this was going to do what I thought it could. Find all of the flat surfaces in the model and apply a workplane to each in the proper orientation. Wishful thinking I guess because really what is the right orientation until we tell it what it is.