-- ArchiFrame BTL cnc-writer -- See www.lua.org for Lua manual. -- Extension functions - see manual --------------------------------------------------------------------------- -- CODE SHARED WITH ARCHIFRAME START --> EPS=0.0001 PI=3.141592653589793 PI2=PI/2.0 PI180=PI/180.0 PI1800=PI/1800.0 -- # Frame machinings types EMcFrAngledBegOld = 100 EMcFrAngledBeg = 101 EMcFrAngledBegTenon = 110 -- Also dovetail EMcFrBegHiddenShoe = 111 EMcVCutBeg = 114 EMcFrAngledEndOld = 200 EMcFrAngledEnd = 201 EMcFrAngledEndTenon = 210 EMcFrEndHiddenShoe = 211 EMcVCutEnd = 214 EMcFrOpening = 300 -- Opening - unused now EMcFrGroove = 301 -- Logsin tapainen vapaa ura EMcFrDrill = 302 -- Drilling EMcFrMarking = 303 -- Marking EMcFrReinforce = 304 EMcFrSaw = 305 EMcFrNailGroup = 306 EMcFrNailLine = 307 EMcFrTenonSide = 400 -- Also dovetail EMcFrBalkJoint = 401 -- Narrowed balk EMcFrBalkShoe = 1000 --------------------------------------------------------------------------- -- ARCHIFRAME & ARCHILOGS COMMON FUNCTIONS function toLog(str) AddCncErr( 0, str ) end -- Dumps given variable into string function DumpTblInt(o) local s if type(o) == 'table' then s = '{ ' for k,v in pairs(o) do if type(k) ~= 'number' then k = '"'..k..'"' end s = s .. '['..k..'] = ' .. DumpTblInt(v) .. ',' end s=s .. '} ' else s=tostring(o) end return s end function DumpTbl(o) ac_environment("tolog", DumpTblInt(o)) end -- Returns 32bit binary number string "010011100..." function NumToBin(nVal) local nExp, sBin nExp=2147483648 sBin="" repeat if nVal+0.1>=nExp then nVal=nVal-nExp sBin=sBin .. "1" else sBin=sBin .. "0" end nExp=nExp/2 until nExp<0.9 if string.len(sBin)~=32 then error( "NumToBin failed" ) end return sBin end function BitTest(nVal, nBit0based) local s, s2 s=NumToBin(nVal) s2=string.sub(s, 32-nBit0based, 32-nBit0based) if s2=="0" then return 0 end if s2=="1" then return 1 end error( string.format("BitTest failed (s=%s, s2=%s)", s, s2) ) end -- Removes spaces from beg and end function TrimStr(str) local res, start, endIndex, len len=string.len(str) start=1 while start<=len and string.sub(str,start,start)==" " do start=start+1 end endIndex=len while endIndex>0 and string.sub(str,endIndex,endIndex)==" " do endIndex=endIndex-1 end return string.sub(str,start, endIndex) end -- Aiheuttaa virheilmoituksen, jonka Logs osaa tulkita k�ytt�j�lle n�ytett�v�ksi viestiksi -- Ilmoitus voi olla tyhj�, jolloin ei n�ytet� mit��n (peruttu) function RaiseError(msg) msg="###>" .. msg .. "<###" error( msg ) end gnCncErrCount=0 -- Notices for the log -- Adds cnc error message to gTblCncErr function AddErrMsg(sLogId, nPos, sText) if nPos==nil then error("AddCncErr/nPos") end if sText==nil then error("AddCncErr/sText") end if gnCncErrCount==0 then gTblCncErr={} end gnCncErrCount=gnCncErrCount+1 gTblCncErr[gnCncErrCount] = {} gTblCncErr[gnCncErrCount].guid=gsCurrentLogGuid gTblCncErr[gnCncErrCount].logid=sLogId gTblCncErr[gnCncErrCount].pos=nPos gTblCncErr[gnCncErrCount].text=sText end -- Adds cnc error message to gTblCncErr function AddCncErr(nPos, sText) AddErrMsg(gsCurrentLogId, nPos, sText) end gtblUsedIds = {} -- T�nne asetetaan ["id"]=1, ett� l�ydet��n samalla ID:ll� olevat hirret function CheckId( sId ) if sId=="" then AddCncErr( 0, string.format("LOG/FRAME EMPTY ID FOUND") ) elseif gtblUsedIds[sId]~=null then AddCncErr( 0, string.format("LOG/FRAME ID %s USED AT LEAST TWICE", sId) ) else gtblUsedIds[sId]=1 end end -- User entered values may be small if millimeters were used function NormalizeType(nNum) if math.abs(nNum)<0.1 then nNum=math.floor(nNum*1000+0.5) end return nNum end --------------------------------------------------------------------------- -- ARCHIFRAME & ARCHILOGS GEOMETRY FUNCTIONS -- ### Alustaa muuttujan tranTbl[] py�ritt�m��n ja siirt�m��n pistett� -- Katso http://gregs-blog.com/category/3d-mathematics/ -- in: -- vecX,vecY,vecZ Vektori, jonka ymp�rill� py�ritell��n. Yksikk�vektori!! -- addX,addY,addZ Siirto -- angle Py�rityskulma -- out: -- tranTbl[12] function Rotate3Dinit(vecX, vecY, vecZ, addX, addY, addZ, angle) local cosAngle, sinAngle, ucosAngle, tranTbl cosAngle=math.cos(angle) sinAngle=math.sin(angle) ucosAngle=1-cosAngle tranTbl={} -- Tehd��n yksikk�vektori --_ulen=1/sqr(vecX*vecX+vecY*vecY+vecZ*vecZ) --vecX=vecX*_ulen --vecY=vecY*_ulen --vecZ=vecZ*_ulen tranTbl[1]=ucosAngle*vecX*vecX+cosAngle tranTbl[2]=ucosAngle*vecX*vecY-vecZ*sinAngle tranTbl[3]=ucosAngle*vecZ*vecX+vecY*sinAngle tranTbl[4]=addX tranTbl[5]=ucosAngle*vecX*vecY+vecZ*sinAngle tranTbl[6]=ucosAngle*vecY*vecY+cosAngle tranTbl[7]=ucosAngle*vecY*vecZ-vecX*sinAngle tranTbl[8]=addY tranTbl[9]=ucosAngle*vecZ*vecX-vecY*sinAngle tranTbl[10]=ucosAngle*vecY*vecZ+vecX*sinAngle tranTbl[11]=ucosAngle*vecZ*vecZ+cosAngle tranTbl[12]=addZ --tranTbl[13]=0 --tranTbl[14]=0 --tranTbl[15]=0 --tranTbl[16]=1 return tranTbl end -- ### Py�ritt�� 3D-pistett� annetun vektorin ymp�ri. Kutsu Rotate3Dinit ennen t�t�. -- in: -- xin, yin, zin -- out: -- x, y, z function Rotate3D(tranTbl, xin, yin, zin) local x, y, z x=tranTbl[1]*xin + tranTbl[2]*yin + tranTbl[3]*zin + tranTbl[4] y=tranTbl[5]*xin + tranTbl[6]*yin + tranTbl[7]*zin + tranTbl[8] z=tranTbl[9]*xin + tranTbl[10]*yin + tranTbl[11]*zin + tranTbl[12] return x,y,z end -- ### Py�ritt�� 3D-pistett� annetun vektorin ymp�ri pit�en edellisen Rotate3DInit-arvot kuosissa -- in: -- xin, yin, zin, angle -- vecX, vecY, vecZ Mink� vektorin ymp�ri py�ritell��n -- out: -- _x, _y, _z function RotateSingle3D(xin, yin, zin, vecX, vecY, vecZ, angle) local x, y, z, tranTbl tranTbl = Rotate3Dinit(vecX, vecY, vecZ, 0, 0, 0, angle) x,y,z=Rotate3D(tranTbl,xin, yin, zin) return x,y,z end -- Calculates cross product of two vectors -- Katsele http://en.wikipedia.org/wiki/Cross_product: On se suunta t�ll�kin tiedossa: vec1 on oikean k�den etusormi, vec2 keskari ja refRes on sitten peukalo yl�s -- returns x,y,z function CalcCross3D( x1, y1, z1, x2, y2, z2 ) local x,y,z x = y1 * z2 - z1 * y2 y = z1 * x2 - x1 * z2 z = x1 * y2 - y1 * x2 return x,y,z end -- Sets len to 1 -- Returns x,y,z,bOkVec (true/false=zero len) function ToUnitVec3(x, y, z) local d d=math.sqrt(x*x+y*y+z*z) if d<0.0001 then return 0,0,0,false end d=1/d return x*d,y*d,z*d,true end -- Duunaa tason yht�l�n ax+bx+cx+dx=0 tason normaalista ja yhdest� pisteest� x1,y1,z1 -- Huom-- plane constant eri etumerkill� kuin wykobissa -- in: -- tnx, tny, tnz Vektori tason normaalista, lasketaan t��ll� yksikk�vektoriksi -- x1,y1,z1 Piste tasolla -- out: -- ta, tb, tc, td Tason yht�l� (ta,tb,tc=tnx,tny,tnz normalisoituna) function MakePlaneNormal(tnx, tny, tnz, x1, y1, z1) -- template -- inline plane make_plane(const T& x1, const T& y1, const T& z1, -- const T& x2, const T& y2, const T& z2, -- const T& x3, const T& y3, const T& z3) -- { -- plane plane_; -- vector3d v1 = makevector(x2 - x1, y2 - y1, z2 - z1); -- vector3d v2 = makevector(x3 - x1, y3 - y1, z3 - z1); -- plane_.normal = normalize(v1 * v2); -- plane_.constant = dot_product(plane_.normal,makevector(x1,y1,z1)); -- return plane_; -- } local mul, ta, tb, tc, td -- T�ss� hyp�t��n suoraan vakion laskentaan mul=1/math.sqrt(tnx*tnx+tny*tny+tnz*tnz) ta=tnx*mul tb=tny*mul tc=tnz*mul td=-(ta*x1+tb*y1+tc*z1) return ta, tb, tc, td end -- ### Suoran ja tason leikkauspiste -- in: -- ta,tb,tc,td Tason yht�l�, a-c=kohtisuora ja d on vakio -- x1, y1, z1 Suora -- x2, y2, z2 Suora -- out: -- hasX Oliko leikkausta -- x, y, z Piste -- dist Et�isyys pisteest� x1,y1,z1 suoran suuntaisesti function LinePlaneX(ta, tb, tc, td, x1, y1, z1, x2, y2, z2 ) -- Tuollainen koodi "k��nnetty" wykobi-kirjastosta -- template -- inline point3d intersection_point(const line& line, -- const plane& plane) -- { -- vector3d linevec = line[1] - line[0]; -- T denom = dot_product(linevec,plane.normal); -- point3d ipoint = degenerate_point3d(); -- if (not_equal(denom,T(0.0))) -- { -- T t = -distance(line[0],plane) / denom; -- ipoint = line[0] + t * (line[1] - line[0]); -- } -- return ipoint; -- } local x, y, z, dist, denom dist=0 x=x2-x1 y=y2-y1 z=z2-z1 denom=x*ta + y*tb + z*tc -- Pistetulo suora ja tason normaali if math.abs(denom)<1E-5 then -- Tason suuntainen suora return false end -- Pisteen et�isyys tasolta -- (plane.normal.x * point.x + plane.normal.y * point.y + plane.normal.z * point.z ) - plane.constant; dist=-(ta*x1 + tb*y1 + tc*z1 + td)/denom -- Huom-- td eri etumerkill� kuin wykobissa (kun siell� oli puki) x=x1 + x*dist y=y1 + y*dist z=z1 + z*dist return true, x, y, z, dist end -- Returns unit vector 90 degrees left x,y for given vector function GetUnitVecLeft(x, y) local len len=math.sqrt(x*x+y*y) if lenmaxEt then maxEt=cutOffsets[i] end end return cutOffsets, maxEt end -- Calculates intersection of two infinite lines -- Line1 is (x1,y1)->(x2,y2) and line2 is (x3,y3)->(x4,y4) -- See http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline2d/ -- Returns three values: -- 1. bool, true=has intersection -- 2. number, x -- 3. number, y function GetLinesX( x1, y1, x2, y2, x3, y3, x4, y4 ) local ua, divisor divisor=(y4-y3)*(x2-x1)-(x4-x3)*(y2-y1) if math.abs(divisor)PI180 and nBevel>=-PI1800 then -- Just bevel cut (and not 90 deg +-1 deg), check if it reaches begin local dx, dy dx=gnCurrTotLen-xoff dy=dx*math.abs(math.tan(nBevel)) if nBevelPI then nAngle=-2*PI+nAngle elseif nAngle<-PI then nAngle=2*PI+nAngle end s = string.format( "PROCESSKEY: %d-013-%d SAW CUT\n" .. "PROCESSPARAMETERS: P01:%08.0f P02:%08.0f P03:00000000 P06:%08.0f P07:%08.0f P08:%08.0f P11:%08.0f P12:%08.0f", 4, -- X-013-1 nBtlSide, -- 1-013-X xc * 10000.0, -- P01 yc * 10000.0, -- P02 nAngle / PI * 1800, -- P06 nBevel / PI * 1800, -- P07 angleBottom / PI * 1800, -- P08 depth * 10000.0, -- P11 len * 10000.0 -- P12 ) end BtlAddMc( xc, s ) return isBeg end -- Rotates refline to next side function BtlGroRotateToNext(x1, y1, x2, y2, groDepth, groWidth, groAngle, groAngleLen) local tblGro tblGro={} tblGro.guid = gsCurrentLogGuid tblGro.x1 = x1 tblGro.y1 = y1 tblGro.x2 = x2 tblGro.y2 = y2 tblGro.depth = groDepth tblGro.width = groWidth tblGro.groAngleRad = groAngle tblGro.face = groFace tblGro.groTiltAngleRad = groAngleLen af_request("mc_rotgro", tblGro) return tblGro.x1, tblGro.y1, tblGro.x2, tblGro.y2, tblGro.depth, tblGro.width, tblGro.groAngleRad, tblGro.groTiltAngleRad end -- Changes dir of refline (FrameMc.cpp) function BtlGroSwapDir(x1, y1, x2, y2, groDepth, groWidth, groAngle, groAngleLen) local x, y, d, dx, dy, groLen local nx1, ny1, nx2, ny2, ngroDepth, ngroAngle, ngroAngleLen dx=x2-x1 dy=y2-y1 groLen = math.sqrt( dx*dx + dy*dy ) -- T�m� on uran pituus pinnassa, pohjalla voi olla pienempi nx1 = x2 ny1 = y2 nx2 = x1 ny2 = y1 -- Pitkitt�iskallistuksen vaikutus, pos tiltAngle syvent�� uraa ngroDepth = groDepth + groLen * math.sin(groAngleLen) ngroAngle = groAngle; ngroAngleLen = -groAngleLen return nx1, ny1, nx2, ny2, ngroDepth, groWidth, ngroAngle, ngroAngleLen end -- Calculates parameters for middle line function BtlGroCalcMid(x1, y1, x2, y2, groDepth, groWidth, groAngle, groAngleLen) local x, y, d local nx1, ny1, nx2, ny2, ngroDepth, ngroWidth, ngroAngle, ngroAngleLen nx1, ny1, nx2, ny2, ngroDepth, ngroWidth, ngroAngle, ngroAngleLen = BtlGroRotateToNext(x1, y1, x2, y2, groDepth, groWidth, groAngle, groAngleLen) nx1, ny1, nx2, ny2, ngroDepth, ngroWidth, ngroAngle, ngroAngleLen = BtlGroRotateToNext(nx1, ny1, nx2, ny2, ngroDepth, ngroWidth, ngroAngle, ngroAngleLen) nx1, ny1, nx2, ny2, ngroDepth, ngroWidth, ngroAngle, ngroAngleLen = BtlGroSwapDir(nx1, ny1, nx2, ny2, ngroDepth, ngroWidth, ngroAngle, ngroAngleLen) return (nx1+x1)*0.5, (ny1+y1)*0.5, (nx2+x2)*0.5, (ny2+y2)*0.5, (ngroDepth + groDepth)*0.5, groWidth, groAngle, groAngleLen end -- Adds groove as birds mouth if possible -- Returns: true=saved, false=not saved function BtlAddGroAsBird(sPieceNum, x1, y1, x2, y2, groDepth, groWidth, groAngle, groFace, groAngleLen) local d, dxDir, dyDir, dzDir, dxDown, dyDown, dzDown, dxBot, dyBot, dzBot, yBot2 local x,y,z, xBot, yBot, zBot, nAngle7, nAngle8, nBegEnd, faceDy --AddCncErr( x1, string.format("x1=%f, y1=%f, x2=%f, y2=%f, groDepth=%f, groWidth=%f, groAngle=%f, groAngleLen=%f", x1, y1, x2, y2, groDepth, groWidth, groAngle, groAngleLen) ) -- Not to beg/end if groFace>4 then return false end -- Must be tilted and lengthwise IS supported if math.abs(groAngle)PI/180 then return false end local tblGroInfo, tblGro tblGro={} tblGro.guid = gsCurrentLogGuid tblGro.x1 = x1 tblGro.y1 = y1 tblGro.x2 = x2 tblGro.y2 = y2 tblGro.depth = groDepth tblGro.width = groWidth tblGro.groAngleRad = groAngle tblGro.face = groFace tblGro.groTiltAngleRad = groAngleLen if y1>y2 then -- To have positive angle af_request("mc_swapgroref", tblGro) x1 = tblGro.x1 y1 = tblGro.y1 x2 = tblGro.x2 y2 = tblGro.y2 groDepth = tblGro.depth groAngle = tblGro.groAngleRad groAngleLen = tblGro.groTiltAngleRad end tblGroInfo = af_request("mc_getgroinfo", tblGro) if tblGroInfo==nil or tblGroInfo.isBird==false then return false end -- On surface, z neg=inside the plank dxDir=(x2-x1) dyDir=(y2-y1) dzDir=0 dxDir,dyDir,dzDir,d=ToUnitVec3(dxDir,dyDir,0) if d==false then return false end -- To left from surface dxBot,dyBot,dzBot=RotateSingle3D(-dyDir, dxDir, 0, dxDir, dyDir, 0, groAngle) dxDir,dyDir,dzDir=RotateSingle3D(dxDir, dyDir, 0, dxBot,dyBot,dzBot, groAngleLen) dxDown,dyDown,dzDown=CalcCross3D(dxBot, dyBot, dzBot, dxDir, dyDir, dzDir) --ac_msgbox(string.format("dxDir=%f, dyDir=%f, dzDir=%f, dxDown=%f, dyDown=%f, dzDown=%f, dxBot=%f, dyBot=%f, dzBot=%f", dxDir, dyDir, dzDir, dxDown, dyDown, dzDown, dxBot, dyBot, dzBot)) -- Calculate groove bottom local angleBeg, angleEnd local xBot1, yBot1, zBot1, xBot2, yBot2, zBot2 local nLeft, begZ, endZ xBot1=x1+dxDown*groDepth yBot1=y1+dyDown*groDepth zBot1=0-dzDown*groDepth xBot2=xBot1+dxBot*groWidth yBot2=yBot1+dyBot*groWidth zBot2=zBot1-dzBot*groWidth --AddCncErr(0, string.format("xBot1=%f yBot1=%f zBot1=%f xBot2=%f yBot2=%f zBot2=%f", xBot1, yBot1, zBot1, xBot2, yBot2, zBot2)) --AddCncErr( x1, string.format("x1=%f, y1=%f, x2=%f, y2=%f, groDepth=%f, groWidth=%f, groAngle=%f, groAngleLen=%f", x1, y1, x2, y2, groDepth, groWidth, groAngle, groAngleLen) ) -- Calc side projection angles (valid angle for birdmouth 1...89 deg) nBegEnd=4 if tblGroInfo.birdBegCutFace==2 then angleBeg=math.atan2(dzBot, -dxBot) -- Positive if cutting the wood (should be always). This is on plank side surface - not on the groove. xBot=xBot1 yBot=yBot1 zBot=zBot1 else angleBeg=math.atan2(-dzDown, dxDown) xBot=xBot2 yBot=yBot2 zBot=zBot2 end if tblGroInfo.birdEndCutFace==2 then angleEnd=math.atan2(-dzBot, -dxBot) -- Positive if cutting the wood (should be always) else angleEnd=math.atan2(-dzDown, -dxDown) end if angleBegPI*89/180 or angleEndPI*89/180 then -- Can be v-cut return false end if groAngle>0 then nAngle7=PI2-groAngle nAngle8=nAngle7+PI2 else nAngle7=-groAngle nAngle8=nAngle7+PI2 end if nAngle7PI*89/180 or nAngle8PI-PI/180 then -- Prev check should disable this AddCncErr( x1, string.format("BTL ERROR: FAILED TO CONVERT GROOVE TO BIRDS MOUTH - SAVED AS GROOVE (nAngle7=%f, nAngle8=%f, groAngle=%f)", nAngle7, nAngle8, groAngle) ) return false end nAngle8=PI-angleBeg nAngle7=nAngle8-PI2 -- Ok bird's mouth and bottom beg calculated local nBtlSide, bMirrorY local nGroLen, nAngle, dx, dy dx=x2-x1 dy=y2-y1 nGroLen=math.sqrt(dx*dx+dy*dy)*math.cos(groAngleLen) -- ON GROOVE BOTTOM nAngle=math.atan2(dyDir, dxDir) if nBegEnd==4 then nAngle=PI-nAngle end ------------------------------------- -- Adjust to keep inside the plank. dyDir is always positive local xoff, yoff, zoff if yBot<0 and dyDir>0.001 then yoff=-yBot yBot=0 xoff=dxDir/dyDir*yoff xBot=xBot+xoff zoff=dzDir/dyDir*yoff zBot=zBot-zoff nGroLen=nGroLen-math.sqrt(xoff*xoff+yoff*yoff+zoff*zoff) end faceDy=gnCurrWidth if groFace==2 or groFace==4 then faceDy=gnProfHeight end yoff=yBot+nGroLen*dyDir-faceDy if yoff>0 then xoff=dxDir/dyDir*yoff zoff=dzDir/dyDir*yoff nGroLen=nGroLen-math.sqrt(xoff*xoff+yoff*yoff+zoff*zoff) end -- Adjust to keep inside the plank. ------------------------------------- if nAnglePI-PI/180 then return false end nBtlSide,bMirrorY=BtlGetSide14(groFace) x=BtlGetXc(xBot) y=yBot yBot2=nGroLen*dyDir -- Actually len on surface --ac_msgbox(string.format("x=%f, y=%f, zBot=%f, yBot2=%f, bMirrorY=%s, nGroLen=%f", x, y, zBot, yBot2, tostring(bMirrorY), nGroLen)) z=-dzDir if groFace==3 or groFace==4 then -- Swap beg/end points x=BtlGetXc(xBot+nGroLen*dxDir) y=yBot+nGroLen*dyDir zBot=zBot-nGroLen*dzDir nAngle=PI-nAngle z=-z end if bMirrorY then y=BtlMirrorYc(y, groFace) end --AddCncErr( x1, string.format("dzDir=%f z=%f, bMirrorY=%s)", dzDir, z, tostring(bMirrorY)) ) --ac_msgbox( string.format("2: x1=%f, y1=%f, x2=%f, y2=%f, groDepth=%f, groWidth=%f, groAngle=%f, groAngleLen=%f", x1, y1, x2, y2, groDepth, groWidth, groAngle, groAngleLen) ) s = string.format( "PROCESSKEY: %d-020-%d BIRDS MOUTH\n" .. "PROCESSPARAMETERS: P01:%08.0f P02:%08.0f P05:00000000 P06:%08.0f P07:%08.0f P08:%08.0f P09:00000000 P10:00000000 P11:%08.0f P12:%08.0f P13:%08.0f P14:00000000 P15:00000000", nBegEnd, -- X-020-1 nBtlSide, -- 3-020-X x * 10000.0, -- P01 y * 10000.0, -- P02 nAngle / PI * 1800, -- P06 nAngle7 / PI * 1800, -- P07 nAngle8/ PI * 1800, -- P08 zBot * 10000.0, -- P11 (zBot + nGroLen*z) * 10000.0, -- P12 yBot2 * 10000.0 -- P13 ) BtlAddMc( x, s ) if gbBtlMarkReinforce then -- Add also marking lines to the bottom side local x1, y1, y1_2 local x2, y2, y2_2 x1, y1, y1_2=BtlAddBirdMark( tblGroInfo.projside1, sPieceNum ) x2, y2, y2_2=BtlAddBirdMark( tblGroInfo.projside2, sPieceNum ) if x1 and x2 and math.abs(x2-x1)<0.001 then if y1>y1_2 then y1,y1_2=y1_2,y1 end if y2>y2_2 then y2,y2_2=y2_2,y2 end if y2>y1 then y1=y2 elseif y1>y2 then y2=y1 end if y2_20 then dy=0 if y4 then if math.abs(x2-x1)>math.abs(y1-y2) then if x1>x2 then bSwapLR=true end elseif y1>y2 then bSwapLR=true end elseif y1>y2 then bSwapLR=true end if bSwapLR then --AddCncErr( x1, "y1>y2" ) af_request("mc_swapgroref", tblGro) x1 = tblGro.x1 y1 = tblGro.y1 x2 = tblGro.x2 y2 = tblGro.y2 groDepth = tblGro.depth groAngle = tblGro.groAngleRad groAngleLen = tblGro.groTiltAngleRad end tblGroInfo = af_request("mc_getgroinfo", tblGro) if tblGroInfo==nil then return false end if tblGroInfo.vHun~=1 then return false end -- To ends try always, for other sides use only if double angle if groFace<5 and (math.abs(groAngle)0.0099 or groFace>=5 or groAngleLen>EPS then return false end local nBtlSide,bMirrorY, len, dx, dy, nAngle, s x1, y1, x2, y2, groDepth, groWidth, groAngle, groAngleLen = BtlGroCalcMid(x1, y1, x2, y2, groDepth, groWidth, groAngle, groAngleLen) nBtlSide,bMirrorY=BtlGetSide14(groFace) dx=x2-x1 dy=y2-y1 len=math.sqrt(dx*dx+dy*dy) --ac_environment("tolog", string.format("dx=%f, dy=%f, x1=%f, y1=%f, x2=%f, y2=%f", dx, dy, x1, y1, x2, y2)) if bMirrorY then y1=BtlMirrorYc(y1, groFace) dy=-dy groAngle=-groAngle groAngleLen=-groAngleLen -- Not supported now end nAngle=math.atan2( dy, dx ) nAngle=PI-nAngle if nAngle>PI then nAngle=-2*PI+nAngle elseif nAngle<-PI then nAngle=2*PI+nAngle end x1=BtlGetXc(x1) s = string.format( "PROCESSKEY: %d-013-%d SAW CUT\n" .. "PROCESSPARAMETERS: P01:%08.0f P02:%08.0f P03:00000000 P06:%08.0f P07:%08.0f P08:%08.0f P11:%08.0f P12:%08.0f", 0, -- X-013-1 nBtlSide, -- 1-013-X x1 * 10000.0, -- P01 y1 * 10000.0, -- P02 nAngle / PI * 1800, -- P06 (PI2 - groAngle) / PI * 1800, -- P07 groAngleLen / PI * 1800, -- P08 (anglelen) groDepth * 10000.0, -- P11 len * 10000.0 -- P12 ) BtlAddMc( x1, s ) return true end function BtlDirToAngles(dirUpX, dirUpY, dirUpZ) local rotZ=0.0 if math.abs(dirUpX)>EPS or math.abs(dirUpY)>EPS then rotZ=math.atan2(dirUpY, -dirUpX) if rotZ<0.0 then rotZ=PI*2+rotZ end end return rotZ, PI-math.asin(dirUpZ) end function BtlAddGroAsDrilling(sPieceNum, x1, y1, x2, y2, groDepth, groWidth, groAngle, groFace, groAngleLen) if groFace==5 or groFace==6 then local dx=x2-x1 local dy=y2-y1 local length=math.sqrt(dx*dx+dy*dy) local ux=dx/length local uy=dy/length local x=x1+ux*length/2-uy*groWidth/2 local y=y1+uy*length/2+ux*groWidth/2 local diameter=TernaryIf(lengthgnCurrTotLen+1 or y1<-1 or y1>faceHeight+1 or x2<-1 or x2>gnCurrTotLen+1 or y2<-1 or y2>faceHeight+1 or groDepth>1 then AddCncErr( x1, string.format("WARNING: GROOVE WITH SUSPICIOUS BIG COORDINATES/DEPTH - WRITTEN INTO BTL FILE ANYWAY: x1=%.0f y1=%.0f x2=%.0f y2=%.0f depth=%.0f", x1*1000, y1*1000, x2*1000, y2*1000, groDepth*1000) ) end -- ## Weinmann hack: If at the end, leave 1 mm uncut to avoid Weinmann doing the groove with the saw blade if gbBtlMoveGroFromEnd and groFace<5 and math.abs(x1-x2)faceHeight-0.001 then local s s=ac_objectget("iElemGroup") if string.match(s, "^bottom_force.*") or string.match(s, "^2ndbottom.*") or string.match(s, "^top_force.*") or string.match(s, "^2ndtop.*") then -- Straight full groove if x1-groWidth0 and groWidthgnCurrTotLen-EPS then groWidth=gnCurrTotLen-(x1-groWidth)-0.001 x1=gnCurrTotLen-0.001 x2=x1 if gnBtlMinGroWidth>0 and groWidth4 then --gnBtlPlateSide=EBtlMale --ac_environment( "tolog", string.format("GRO %d %s", groFace, gsCurrentLogId)) local ngroDepth, nBegEnd, pos -- Beg&End, adjust to midline, 017 takes groove middle point as refence x1, y1, x2, y2, groDepth, groWidth, groAngle, groAngleLen = BtlGroCalcMid(x1, y1, x2, y2, groDepth, groWidth, groAngle, groAngleLen) if groWidth0.0001 then -- Non-vertical groove if math.abs(groAngle)>PI/180 then --ac_environment("tolog", string.format("x1=%f y1=%f x2=%f y2=%f\n", x1, y1, x2, y2) ) if x1<0.001 and x2>gnCurrWidth-0.001 and math.abs(groAngleLen)PI/180 then AddCncErr( pos, string.format("ERROR: LENGTHWISE TILTED GROOVE NOT VERTICAL - ANGLE RESETED") ) groAngleLen=0 end end -- Depth at middle point groDepth = groDepth + nGroLen * 0.5 * math.sin(groAngleLen) nBtlSide=BtlGetSide14(3) -- Anchor to bottom side nBegEnd=4 if groFace==6 then nBegEnd=3 end nGroLen=nGroLen * math.cos(groAngleLen) -- Len at bottom nAngle=-math.atan2(dy, dx) if nAngle<0 then nAngle=2*PI+nAngle end if nAngle>PI then -- Change from 180...360 deg to 0...180 nAngle=nAngle-PI end if (nBtlSide==1 or nBtlSide==3) and math.abs(groAngle)PI then -- Change from 180...360 deg to 0...180 nAngle=nAngle-PI end end if math.abs(nAngle)gnCurrWidth then -- groDepth=0 -- end local tblGroInfo, tblGro, distFromSurf, tblGroSideIn, nGroSideIn -- Get groove info now for sides outside the plank tblGro={} tblGro.guid = gsCurrentLogGuid tblGro.x1 = x1 tblGro.y1 = y1 tblGro.x2 = x2 tblGro.y2 = y2 tblGro.depth = groDepth tblGro.width = groWidth tblGro.groAngleRad = groAngle tblGro.face = groFace tblGro.groTiltAngleRad = groAngleLen tblGro.vCutToCurrent=1 tblGroInfo = af_request("mc_getgroinfo", tblGro) if groWidthPI-PI/180.0 then -- Limited to 1...179 deg if recCount==nil then -- First try swapping dir 180 deg recCount=0 x1, y1, x2, y2, groDepth, groWidth, groAngle, groAngleLen = BtlGroRotateToNext(orgX1, orgY1, orgX2, orgY2, groDepth, groWidth, groAngle, groAngleLen, groFace) x1, y1, x2, y2, groDepth, groWidth, groAngle, groAngleLen = BtlGroRotateToNext(x1, y1, x2, y2, groDepth, groWidth, groAngle, groAngleLen, groFace) BtlAddGro(sPieceNum, x1, y1, x2, y2, groDepth, groWidth, groAngle, groFace, groAngleLen, groType, recCount) return else if recCount==4 then AddCncErr( x1, string.format("ERROR: BTL CNC WRITER COULD NOT SWAP GROOVE DIR - MAY CAUSE ERRORS LATER (2)") ) else x1, y1, x2, y2, groDepth, groWidth, groAngle, groAngleLen = BtlGroRotateToNext(orgX1, orgY1, orgX2, orgY2, groDepth, groWidth, groAngle, groAngleLen, groFace) BtlAddGro(sPieceNum, x1, y1, x2, y2, groDepth, groWidth, groAngle, groFace, groAngleLen, groType, recCount+1) return end end end --DumpTbl(tblGroInfo.vecDownSurf) --AddCncErr( x1, string.format("distFromSurf=%f", distFromSurf) ) if groFace==3 or groFace==4 then -- Need to swap the ref side x1, y1, x2, y2, groDepth, groWidth, groAngle, groAngleLen = BtlGroRotateToNext(orgX1, orgY1, orgX2, orgY2, groDepth, groWidth, groAngle, groAngleLen, groFace) x1, y1, x2, y2, groDepth, groWidth, groAngle, groAngleLen = BtlGroRotateToNext(x1, y1, x2, y2, groDepth, groWidth, groAngle, groAngleLen, groFace) x1, y1, x2, y2, groDepth, groWidth, groAngle, groAngleLen = BtlGroSwapDir(x1, y1, x2, y2, groDepth, groWidth, groAngle, groAngleLen) x1=BtlGetXc(x1) x2=BtlGetXc(x2) if bMirrorY then y1=BtlMirrorYc(y1, groFace) y2=BtlMirrorYc(y2, groFace) end nGroSideIn=3 -- Start from end end if math.abs(groAngle)>PI/1800 and math.abs(groAngleLen)>PI/1800 then AddCncErr( x1, string.format("ERROR: DOUBLE ANGLED GROOVE - NOT SUPPORTED AND SKIPPED ***") ) return end local tblGro2, tblGroInfo2, tblGroInfo3 tblGro2={} tblGro2.guid = gsCurrentLogGuid tblGro2.x1 = x1 tblGro2.y1 = y1 tblGro2.x2 = x2 tblGro2.y2 = y2 tblGro2.depth = groDepth tblGro2.width = groWidth tblGro2.groAngleRad = groAngle tblGro2.face = groFace tblGro2.groTiltAngleRad = groAngleLen tblGro2.vCutToCurrent=1 tblGroInfo2 = af_request("mc_getgroinfo", tblGro2) distFromSurf=-groDepth*tblGroInfo2.vecDownSurf.z -- Positive inside the plank tblGro2.groAngleRad = 0 tblGroInfo3 = af_request("mc_getgroinfo", tblGro2) x1=x1+groDepth*tblGroInfo3.vecDownSurf.x -- Projected to surface (only lengthwise) y1=y1+groDepth*tblGroInfo3.vecDownSurf.y --toLog( string.format("2: x1=%f, y1=%f, x2=%f, y2=%f, groDepth=%f, groWidth=%f, groAngle=%f, groAngleLen=%f", x1, y1, x2, y2, groDepth, groWidth, groAngle, groAngleLen) ) dx=x2-x1 dy=y2-y1 nGroLen=math.sqrt(dx*dx+dy*dy) -- dx and dy unused after this -- # P4: which sides are open/not inside the plank p4=32 -- Change 9/2019: Mark it as open upwards: 0 -- Begin if tblGroSideIn[nGroSideIn]==0 then p4=p4+4 end nGroSideIn=(nGroSideIn%4)+1 -- Ref if tblGroSideIn[nGroSideIn]==0 then p4=p4+2 end nGroSideIn=(nGroSideIn%4)+1 -- End if tblGroSideIn[nGroSideIn]==0 then p4=p4+8 end nGroSideIn=(nGroSideIn%4)+1 -- Left if tblGroSideIn[nGroSideIn]==0 then p4=p4+1 end local faceDy faceDy=gnProfHeight if groFace==1 or groFace==3 then faceDy=gnCurrWidth end if math.abs(y1)<0.0001 then y1=0 end if math.abs(y1-faceDy)<0.0001 then y1=faceDy end --toLog( string.format("2: x1=%f, y1=%f, x2=%f, y2=%f, groDepth=%f, groWidth=%f, groAngle=%f, groAngleLen=%f", x1, y1, x2, y2, groDepth, groWidth, groAngle, groAngleLen) ) if math.abs(tblGroInfo2.vecLenSurf.y)>0.010 then if tblGroInfo.begIn==0 then -- Move y1 to zero local y y=-y1 if tblGroInfo2.vecLenSurf.y<0 then y=-y end x1=x1 + y * tblGroInfo2.vecLenSurf.x/tblGroInfo2.vecLenSurf.y distFromSurf=distFromSurf - y * tblGroInfo2.vecLenSurf.z/tblGroInfo2.vecLenSurf.y y1=0 end if tblGroInfo.endIn==0 and y2>y1 and y20.0001 then x,y=ac_geo("linex", x1, y1, x2, y2, tblLine.x1, tblLine.y1, tblLine.x2, tblLine.y2) if x then dist, distStart,len=ac_geo("linedist", x1, y1, x2, y2, x, y) if distStart>-EPS and distStart-EPS and distStart4 then AddCncErr( 0, string.format("WARNING - MARKING AT BAD SIDE (%d) SKIPPED", groFace) ) return end if not bLine and strText=="" then return end if strText~=nil and strText~="" and math.abs(x2-x1)>0.001 and math.abs(y2-y1)y2 then y1,y2=y2,y2 end dy=gnProfHeight-y2 if y1>dy then dy=y1 end if dy>gnProfHeight*0.5-0.001 then dy=gnProfHeight*0.5-0.001 end y1=dy y2=gnProfHeight-dy end -- Second thought 12/2014: Let those come: --if bIsStop and groFace~=4 then -- Machine cannot print to these sides -- return --end nBtlSide,bMirrorY=BtlGetSide14(groFace) x1=BtlGetXc(x1) x2=BtlGetXc(x2) if bMirrorY then y1=BtlMirrorYc(y1, groFace) y2=BtlMirrorYc(y2, groFace) end if y1>y2+0.0001 then y1,y2=y2,y1 x1,x2=x2,x2 end -- Max height for the text if groFace==1 or groFace==3 then dy=gnCurrWidth else dy=gnProfHeight-gnBtlYoffset end fontSize=fontSize/100 -- From cm to meter if fontSize>dy*0.8 then fontSize=dy*0.8 -- Machine will determine the height end -- Build bits bitMask=1 if bLine then bitMask=0 end if strText~="" then if yPosText==1 then -- bottom bitMask=bitMask+2 elseif yPosText==3 then -- Top bitMask=bitMask+8 else -- Mid bitMask=bitMask+4 end if xPosText==1 then -- left bitMask=bitMask+16 elseif xPosText==3 then -- right bitMask=bitMask+64 else -- Mid bitMask=bitMask+32 end bitMask=bitMask+128 end nAngle=math.atan2(y2-y1,x2-x1)/PI*180.0 -- In degrees if nAngle>179 and nAngle<181 then -- In plank dir x2-1.1 then -- In plank dir x2>x1 if bLine then s = string.format( "PROCESSKEY: 4-060-%d MARKING\n" .. "PROCESSPARAMETERS: P01:%08.0f P02:%08.0f P04:%08d P06:00000900 P07:00000900 P11:%08.0f P12:%08.0f P13:%08.0f%s", nBtlSide, -- 4-040-X x1 * 10000.0, -- P01 y1 * 10000.0, -- P02 bitMask*10, -- P04 (x2-x1) * 10000.0, -- P11 10.0, -- P12, 1mm fontSize * 10000.0, sProcess ) BtlAddMc( x1, s ) end if strText=="" then return end -- Adjust params to have line 90 deg at middle y1=dy*0.5-0.0005 y2=y1+0.001 x1=(x1+x2)*0.5 x2=x1 nAngle=90 --DOES NOT WORK: bitMask=bitMask+1 -- No line nTextParallel=0 -- Will be anyway end if nAngle>80 and nAngle<100 and nTextParallel==1 then -- Turn text and recalc bitmask bitMask=BitTest(bitMask,0) -- Keep line on/off -- xPos and yPos swapped if xPosText==1 then bitMask=bitMask+2 elseif xPosText==3 then bitMask=bitMask+8 else bitMask=bitMask+4 end if yPosText==3 then bitMask=bitMask+16 elseif yPosText==1 then bitMask=bitMask+64 else bitMask=bitMask+32 end bitMask=bitMask+1024 end -- Add text always with this s = string.format( "PROCESSKEY: 4-060-%d MARKING\n" .. "PROCESSPARAMETERS: P01:%08.0f P02:%08.0f P04:%08d P06:%08.0f P07:00000000 P11:00000000 P12:%08.0f P13:%08.0f", nBtlSide, -- 4-040-X x1 * 10000.0, -- P01 y1 * 10000.0, -- P02 bitMask*10, -- P04 nAngle*10, -- P06 (y2-y1) * 10000.0, -- P12 fontSize * 10000.0 ) if strText~="" then s=s .. string.format(" P15:\"%s\"", string.gsub(strText, "\"", "")) end s=s .. sProcess BtlAddMc( x1, s ) end -- nAngleTopDeg, angle from top 0=straight cut function BtlAddBegEndTenon( sPieceNum, nBtlSide, bDove, tenonLen, tenonWidth, tenonHeight, borderBot, nAngleSideDeg, nAngleTopDeg, roundingRadius, angleDoveDeg ) AddCncErr( 0, "ERROR: TENON NOT SUPPORTED - SKIPPED" ) end function TernaryIf ( cond , T , F ) if cond then return T else return F end end function BtlAddVCutUsingSawCuts(nBtlSide, nSide, isBeg, nPos, y, p06, p08) cutDepth = TernaryIf(nSide==1 or nSide==3, gnProfHeight, gnCurrWidth) cutLen = gnCurrTotLen -- Will extend outside plank but perhaps machine understands p06 = -p06 if not isBeg then p06 = TernaryIf(p06 > 0, 180 - p06, -(180 + p06)) p08 = TernaryIf(p08 > 0, 180 - p08, -(180 + p08)) end leftOrientation = 4 -- Begin oriented rightOrientation = 3 -- End oriented if p08 > p06 then leftOrientation, rightOrientation=rightOrientation, leftOrientation end s = string.format( "PROCESSKEY: %d-013-%d SAW CUT\n" .. "PROCESSPARAMETERS: P01:%08.0f P02:%08.0f P03:00000000 P06:%08.0f P07:%08.0f P08:%08.0f P11:%08.0f P12:%08.0f", leftOrientation, -- X-013-1 nBtlSide, -- 1-013-X nPos * 10000.0, -- P01 y * 10000.0, -- P02 10*p06, -- P06 10*90.0, -- P07 0.0, -- P08 cutDepth*10000.0, -- P11 cutLen*10000.0 -- P12 ) BtlAddMc( nPos, s ) s = string.format( "PROCESSKEY: %d-013-%d SAW CUT\n" .. "PROCESSPARAMETERS: P01:%08.0f P02:%08.0f P03:00000000 P06:%08.0f P07:%08.0f P08:%08.0f P11:%08.0f P12:%08.0f", rightOrientation, -- X-013-1 nBtlSide, -- 1-013-X nPos * 10000.0, -- P01 y * 10000.0, -- P02 10*p08, -- P06 10*90.0, -- P07 0.0, -- P08 cutDepth*10000.0, -- P11 cutLen*10000.0 -- P12 ) BtlAddMc( nPos, s ) end -- In ArchiFrame, angle tilt comes before bevel tilting function BtlAddBegEndVCut( sPieceNum, isBeg, nSide, x, y, angleDegR, bevelDegR, angleDegL, bevelDegL ) local nBtlSide, nPos, bMirrorY, height, b, endMul, nBeg, s, p06, p08 nBtlSide,bMirrorY=BtlGetSide14(nSide) b=bMirrorY if isBeg then b=not b end if nSide==3 or nSide==4 then b=not b end if b then p06=bevelDegL p08=bevelDegR else --AF and BTL think the same way here: y=BtlMirrorYc(y, nSide) p06=bevelDegR p08=bevelDegL end -- Set p06 and p08 if isBeg then nPos=x endMul=1 nBeg=2 else nPos=gnCurrTotLen-x endMul=-1 nBeg=1 end if math.abs(angleDegR-90)>0.1 or math.abs(angleDegL-90)>0.1 then AddCncErr( x, "WARNING: V-CUT, ONLY 90 DEG ANGLE SUPPORTED, ANGLE SAVED AS 90 DEG." ) end if nSide==1 or nSide==3 then y=gnCurrWidth*0.5-endMul*y else y=gnProfHeight*0.5-endMul*y end nPos = BtlGetXc(nPos) if p06 <= 1.0 or p08 <= 1.0 then BtlAddVCutUsingSawCuts(nBtlSide, nSide, b, nPos, y, p06, p08) return end s = string.format( "PROCESSKEY: %d-011-%d DOUBLE CUT\n" .. "PROCESSPARAMETERS: P01:%08.0f P02:%08.0f P05:00000000 P06:%08.0f P07:%08.0f P08:%08.0f P09:%08.0f", nBeg, -- X-011-1 nBtlSide, -- 1-011-X nPos * 10000.0, -- P01 y * 10000.0, -- P02 10*p06, -- P06 10*angleDegR, -- P07 10*(180-p08), -- P08 10*angleDegL -- P09 ) BtlAddMc( nPos, s ) end function BtlAddHiddenShoe( isBeg, sPieceNum, holeWidth, holeHeight, holeDepth, groDepth, groWidth, drillLowest, drillFromEnd, drillSize, drillSpacing, drillCount ) AddCncErr(0, string.format("BTL: HIDDEN SHOE NOT SUPPORTED") ) end -- Kun palkki menee kapuloiden l�pi function BtlAddBalkMid(sPieceNum, midPos, width, depthTop, depthBot, depthSides) local xc xc=midPos + width * 0.5 BtlAddGro(sPieceNum, xc, 0, xc, gnCurrWidth, depthTop, width, 0, 1, 0) -- Top BtlAddGro(sPieceNum, xc, 0, xc, gnProfHeight, depthSides, width, 0, 2, 0) -- Front BtlAddGro(sPieceNum, xc, 0, xc, gnCurrWidth, depthBot, width, 0, 3, 0) -- Bottom BtlAddGro(sPieceNum, xc, 0, xc, gnProfHeight, depthSides, width, 0, 4, 0) -- Back end function BtlAddSideJoint( sPieceNum, logFace, bDove, midPos, borderBot, tenonLen, tenonWidth, tenonHeight, sideGap, bottomGap, angleDove, roundingRadius ) AddCncErr( midPos, "ERROR: SIDE JOINT NOT SUPPORTED - SKIPPED" ) end -- Adds window groove -- groStart Start coordinate of the groove (ends to the opening) -- groOtherSideOff Value to add to previous to get to other side function BtlAddBuckGro( sPieceNum, nBtlSide, groStart, groOtherSideOff, buckWidth, buckDepth, openingDepth ) AddCncErr( 0, "ERROR: BUCK GRO NOT SUPPORTED - SKIPPED" ) end -- logFace 1=top, 3=bottom -- nDepth Depth from that face -- x1,x2 In bvn-coordinates -- bUseCutter true=add cutter machinings to both sides, false=nope, just sawing function BtlAddOpening( sPieceNum, logFace, nDepth, x1, x2, bUseCutter ) AddCncErr( 0, "ERROR: OPENING NOT SUPPORTED - SKIPPED" ) end -- xStart Start coord in plank coordinates -- dWidth Width -- dDepthTop A Distance from surface to the cross pt (pos=inside plank, neg=outside) -- dYoffTop B Distance from surface middle to the cross pt in surface y-coordinate direction -- dAngleY2Deg C angle at surface y2 -- dAngleY1Deg D angle at surface y1 function BtlAddSaw( sPieceNum, logFace, xStart, dWidth, dDepthTop, dYoffTop, dAngleY2Deg, dAngleY1Deg ) local nBtlSide, bMirrorY, x1Real, x2Real, temp local nLen, nArea, nSplinter, dzNow, dyFace, dyFace2, nSingleCut, yc, nP4, nAngleRad if logFace>4 then AddCncErr( xStart, string.format("BtlAddSaw, BAD FACE: %d. MACHINING SKIPPED", logFace) ) return end nBtlSide,bMirrorY=BtlGetSide14(logFace) -- Miten osuu puuhun x1Real=BtlGetXc(xStart) if dWidth==0 then x2Real=BtlGetXc(gnCurrTotLen) else x2Real=BtlGetXc(xStart+dWidth) end if x1Real>x2Real then x1Real,x2Real = x2Real,x1Real end dyFace=gnProfHeight dzNow=gnCurrWidth if logFace==1 or logFace==3 then dyFace=gnCurrWidth dzNow=gnProfHeight end dyFace2=dyFace*0.5 nSingleCut=0 if dYoffTop<-dyFace2+0.001 then -- Single cut only dAngleY1Deg=0 nSingleCut=2 elseif dYoffTop>dyFace2-0.001 then dAngleY2Deg=0 nSingleCut=1 end -- Tarkistetaan x-koordinaatit yc=dYoffTop+dyFace2 if bMirrorY then yc=dyFace-yc dAngleY1Deg,dAngleY2Deg = dAngleY2Deg,dAngleY1Deg if nSingleCut==1 then nSingleCut=2 elseif nSingleCut==2 then nSingleCut=1 end end nP4=0 nLen=x2Real-x1Real if x1Real<0.001 and x2Real>gnCurrTotLen-0.001 then nLen=0 -- Kaikki nP4=0 elseif x1Real<0.001 then nP4=3 nLen=x2Real elseif x2Real>gnCurrTotLen-0.001 then nP4=5 nLen=gnCurrTotLen-x1Real else nP4=1 end if nSingleCut==0 then -- Valley or hip s = string.format( "PROCESSKEY: 0-012-%d LONGITUDINAL CUT\n" .. "PROCESSPARAMETERS: P01:%08.0f P02:%08.0f P04:%08d P07:%08.0f P09:%08.0f P11:%08.0f P12:%08.0f P13:00000900 P14:00000900 P15:00000900 P16:00000900", nBtlSide, -- 4-010-X x1Real * 10000.0, -- P01 yc * 10000.0, -- P02 nP4 * 10, -- P04 dAngleY1Deg*10, -- P07 dAngleY2Deg*10, -- P09 dDepthTop * 10000, -- P11 nLen * 10000.0 -- P12 ) BtlAddMc( x1Real, s ) else -- Need to calculate yc to the point where the cut hits the surface if nSingleCut==1 then nAngleRad=dAngleY1Deg else nAngleRad=-dAngleY2Deg end nAngleRad=nAngleRad*PI/180 if math.abs(nAngleRad)dx then xOff=dx bDone=true end x=xMid-dx*0.5+xOff if math.abs(x)<0.001 then x=0.001 end if math.abs(x-gnCurrTotLen)<0.001 then x=gnCurrTotLen-0.001 end BtlAddMarking(sPieceNum, groFace, x, yMid-dy*0.5, x, yMid+dy*0.5, xPosText, yPosText, fontSize, textOff, "", true, 0, true) xOff=xOff+0.650 end if false then -- "Adidas" stripes BtlAddMarking(sPieceNum, groFace, xMid-dx*0.5, yMid-dy*0.5, xMid+dx*0.5, yMid-dy*0.5, xPosText, yPosText, fontSize, textOff, "", true, 0, true, false) BtlAddMarking(sPieceNum, groFace, xMid-dx*0.5, yMid, xMid+dx*0.5, yMid, xPosText, yPosText, fontSize, textOff, "", true, 0, true, false) BtlAddMarking(sPieceNum, groFace, xMid-dx*0.5, yMid+dy*0.5, xMid+dx*0.5, yMid+dy*0.5, xPosText, yPosText, fontSize, textOff, "", true, 0, true, false) end end function BtlWritePlank(unidPlank, nSimilarCount, tblSimilarPos) local res, i BtlGetPlankBasicParams(unidPlank) gnBtlBestPrintSide=4 -- Back side -- BTL (viewer) seems to turn the piece itself if gbBtlCalcFeed then if gnCurrWidth-EPS>gnProfHeight then if gnBtlLogDir==EBtlDirBeg then gnBtlPlateSide=EBtlBackSide gnBtlBestPrintSide=3 else gnBtlPlateSide=EBtlFrontSide gnBtlBestPrintSide=1 end else if gnBtlLogDir==EBtlDirBeg then gnBtlPlateSide=EBtlMale else gnBtlPlateSide=EBtlFemale end end end -- 3/2015: Change feeding direction if there are marking only on unsupported side -- Machine is able to do grooves and markings only to the side which is pointing upwards (back side of AF plank with default feeding dir) gbAnyCnc=false gtblGros={} gtblMarkings={} for i=1,6 do gtblGros[i]=0 gtblMarkings[i]=0 end res=BtlWritePlankInt(unidPlank, nSimilarCount, tblSimilarPos) if gnBtlLogDir==EBtlDirBeg and gnBtlPlateSide==EBtlMale and gtblGros[4]==0 and gtblMarkings[4]==0 and (gtblGros[2]>0 or gtblMarkings[2]>0) then -- Change front side to be pointing upwards gnBtlPlateSide=EBtlFemale gnBtlBestPrintSide=2 -- Front side res=BtlWritePlankInt(unidPlank, nSimilarCount, tblSimilarPos) gnBtlPlateSide=EBtlMale end return res end function BtlMakeGroInfoTbl() local tblGroInfos, i, n, x1, y1, x2, y2, groDepth, groWidth, groAngle, groFace, groAngleLen tblGroInfos={} n=ac_objectget("iMc" ,-1) for i=1,n do nType=NormalizeType(ac_objectget("iMc", i, 1)) if nType==EMcFrGroove then x1 =ac_objectget("iMc", i, 2) y1 =ac_objectget("iMc", i, 3) x2 =ac_objectget("iMc", i, 4) y2 =ac_objectget("iMc", i, 5) groDepth=ac_objectget("iMc", i, 6) groWidth=ac_objectget("iMc", i, 7) groAngle=ac_objectget("iMc", i, 8)*PI/180.0 groFace =ac_objectget("iMc", i, 9) groAngleLen=ac_objectget("iMc", i, 10)*PI/180.0 -- Save groove info to be used to cut reinforcement markings local tblGro tblGro={} tblGro.guid = gsCurrentLogGuid tblGro.x1 = x1 tblGro.y1 = y1 tblGro.x2 = x2 tblGro.y2 = y2 tblGro.depth = groDepth tblGro.width = groWidth tblGro.groAngleRad = groAngle tblGro.face = groFace tblGro.groTiltAngleRad = groAngleLen tblGroInfos[#tblGroInfos+1]=af_request("mc_getgroinfo", tblGro) tblGroInfos[#tblGroInfos].tblGro=tblGro end end return tblGroInfos end -- Add IMP if it is impregnent function BtlGetIdPostStr() local s, res res="" -- s=ac_objectget("iElemGroup") -- if string.match(s, "^bottom_.*") or string.match(s, "^2ndbottom_.*") or string.match(s, "^top_.*") or string.match(s, "^2ndtop_.*") then -- res=res .. " S" -- end s=ac_objectget("iMatId") if string.match(s, "IMP") then res=res .. " IMP" end return res end gtblBtlElemQuant=nil -- BtlGetPlankPackage() uses, key=elem guid, value=elem_quant for the element function BtlGetPlankPackage() local info, item info=af_request("plankinfo") if info.ownerelemguid==nil then return "" end item=gtblBtlElemQuant[info.ownerelemguid] if item==nil then -- Get quant local quant quant=af_request("elem_quantities", info.ownerelemguid, 0) item="" if quant and quant.tblelems[1].xmlutf8 then -- At the end of the definition there is -- Quick&dirty: no real parsing item=string.match(quant.tblelems[1].xmlutf8, " cnc_outputline=\"(.-)\"") if item==nil then item="" else item=ToBtl(item) end end gtblBtlElemQuant[info.ownerelemguid]=item end return item end gnBtlScaleUnit=1 -- 1:SCALEUNIT=1, 2:SCALEUNIT=2 -- Converts metric length to btl-length str (8 digits for now) function BtlLen2Str(val) local s if gnBtlScaleUnit==2 then s=string.format("%08.0f", val*100000) elseif gnBtlScaleUnit==1 then s=string.format("%08.0f", val*10000) else error("Bad gnBtlScaleUnit") end if string.len(s)>8 then if gnWarnings==nil then gnWarnings=0 end if gnWarnings<100 then toLog( string.format("ERROR: COORDINATE TOO FAR FROM ORIGIN (MAX IS 99999999, PLEASE SET USER ORIGIN AS ANCHOR POINT: %s", s) ) gnWarnings=gnWarnings+1 end end return s end -- Taken from main Btl-writer -- Creates string containing plank's position and orientation -- strName is either "TRANSFORMATION" or "REFERENCEPLANE" -- Trashes given "#plankinfo" info-parameter -- plateSide gnBtlPlateSide -- Uses gPlankTranMat for transformations -- Also applies gnOrigX,Y,Z function BtlCreatePosStr(strName, info, plateSide) info.begc.x=info.begc.x-gnOrigX info.begc.y=info.begc.y-gnOrigY info.begc.z=info.begc.z-gnOrigZ -- BTL origin at lower left looking at begin if gnBtlLogDir~=EBtlDirBeg then toLog("ERROR: BAD PLANK FEEDING DIR OR ROTATION, POSITION INFORMATION INCORRECT") end if gPlankTranMat then -- Transform coordinate system info.begc.x, info.begc.y, info.begc.z=MatTransformPt( info.begc.x, info.begc.y, info.begc.z, gPlankTranMat ) info.vecx.x, info.vecx.y, info.vecx.z=MatTransformVec( info.vecx.x, info.vecx.y, info.vecx.z, gPlankTranMat ) info.vecy.x, info.vecy.y, info.vecy.z=MatTransformVec( info.vecy.x, info.vecy.y, info.vecy.z, gPlankTranMat ) info.vecz.x, info.vecz.y, info.vecz.z=MatTransformVec( info.vecz.x, info.vecz.y, info.vecz.z, gPlankTranMat ) --toLog( "transformed to:" ) --toLog( string.format("info.begc.x=%f, info.begc.y=%f, info.begc.z=%f", info.begc.x, info.begc.y, info.begc.z) ) --toLog( string.format("info.vecx.x=%f, info.vecx.y=%f, info.vecx.z=%f", info.vecx.x, info.vecx.y, info.vecx.z) ) --toLog( string.format("info.vecy.x=%f, info.vecy.y=%f, info.vecy.z=%f", info.vecy.x, info.vecy.y, info.vecy.z) ) --toLog( string.format("info.vecz.x=%f, info.vecz.y=%f, info.vecz.z=%f", info.vecz.x, info.vecz.y, info.vecz.z) ) end if plateSide==EBtlFemale then -- Easiest case, just offset origin by half width in the y direction info.begc.x=info.begc.x+info.vecy.x*info.width*0.5 info.begc.y=info.begc.y+info.vecy.y*info.width*0.5 info.begc.z=info.begc.z+info.vecy.z*info.width*0.5 elseif plateSide==EBtlMale then -- A bit trickier, need to flip z vector 180 degrees aswell and adjust origin in both directions info.begc.x=info.begc.x-info.vecy.x*info.width*0.5+info.vecz.x*info.height info.begc.y=info.begc.y-info.vecy.y*info.width*0.5+info.vecz.y*info.height info.begc.z=info.begc.z-info.vecy.z*info.width*0.5+info.vecz.z*info.height info.vecz.x=-info.vecz.x info.vecz.y=-info.vecz.y info.vecz.z=-info.vecz.z elseif plateSide==EBtlFrontSide then -- Even more tricky, need to rotate z-vector negative 90 degrees, cannot just "flip" it as in previous case local zx,zy,zz=RotateSingle3D( info.vecz.x, info.vecz.y, info.vecz.z, info.vecx.x, info.vecx.y, info.vecx.z, -PI/2 ) info.vecz.x=zx info.vecz.y=zy info.vecz.z=zz info.begc.x=info.begc.x-info.vecz.x*info.width*0.5 info.begc.y=info.begc.y-info.vecz.y*info.width*0.5 info.begc.z=info.begc.z-info.vecz.z*info.width*0.5 elseif plateSide==EBtlBackSide then -- As in previous case, but rotation positive 90 degrees. -- Even more tricky since we need to offset the origin by the "new" y-vector, -- (meaning the vector perpendicular to the new z-vector and the x-vector) local zx,zy,zz=RotateSingle3D( info.vecz.x, info.vecz.y, info.vecz.z, info.vecx.x, info.vecx.y, info.vecx.z, PI/2 ) info.vecz.x=zx info.vecz.y=zy info.vecz.z=zz local yx, yy, yz=CalcCross3D( info.vecx.x, info.vecx.y, info.vecx.z, info.vecz.x, info.vecz.y, info.vecz.z ) info.begc.x=info.begc.x-info.vecz.x*info.width*0.5-yx*info.height info.begc.y=info.begc.y-info.vecz.y*info.width*0.5-yy*info.height info.begc.z=info.begc.z-info.vecz.z*info.width*0.5-yz*info.height else error( "BtlCreatePosStr: Unknown orientation" ) end return string.format( "%s: OX:%s OY:%s OZ:%s XX:%s XY:%s XZ:%s YX:%s YY:%s YZ:%s", strName, BtlLen2Str(info.begc.x), BtlLen2Str(info.begc.y), BtlLen2Str(info.begc.z), BtlLen2Str(info.vecx.x), BtlLen2Str(info.vecx.y), BtlLen2Str(info.vecx.z), BtlLen2Str(info.vecz.x), BtlLen2Str(info.vecz.y), BtlLen2Str(info.vecz.z) ) end -- Write ArchiFrame plank BTL-code -- ac_objectopen() must have been called before calling this -- Saves CNC to variable gsCncText function BtlWritePlankInt(unidPlank, nSimilarCount, tblSimilarPos) local s, n, i, nOther, x, nDepth, x1, x2, dx, dy, nWidth, sBin, y, z, sPart, sMat local bBegJoint, bEndJoint, nLeftRight local nBtlSide, bMirrorY local nAngleSide, nStraightSide, nAngleTop, nStraightTop, bLine, sStorey local nFirstStopMark=nil -- Need to add stop mark to begin if there is not one already local nLastStopMark=nil -- Need to add stop mark to end if there is not one already local y1Stop, y2Stop if gnBtlLogDir~=EBtlDirBeg then RaiseError( "BTL writing having plank\'s end as reference point unsupported now" ) end gnCncCount=0 gTblCnc=nil gsCncText="" gsCurrentLogId=ac_objectget("#id") -- kappalenro suoraan ID:st�, jos on pelkk�� numeroa s=ac_objectget("iUsageId") if string.find(gsCurrentLogId, "[^%d]")==nil and gbBvnForceUnid==false then -- Pelkk�� numeroa if gnBtlPieceNum==nil then -- H�t�varana, jos on muitakin kuin pelkki� numeerisia tunnuksia gnBtlPieceNum=100000 end sPieceNum=string.format( "%06s", gsCurrentLogId ) sPart=s else -- ID:ss� muutakin kuin numeroa if gnBtlPieceNum==nil then gnBtlPieceNum=1 end if s~="" then sPart=string.format( "%s (%s)", gsCurrentLogId, s) else sPart=string.format( "%s", gsCurrentLogId) end sPieceNum=string.format( "%06d", gnBtlPieceNum ) gnBtlPieceNum=gnBtlPieceNum+1 end if gBtlForceId~=nil then sPart=gBtlForceId end -- Headers if gnBtlPlateSide==EBtlMale or gnBtlPlateSide==EBtlFemale then y=gnCurrWidth x=gnProfHeight else x=gnCurrWidth y=gnProfHeight end sStorey="" -- Was used by Birkeland: -- if gnCurrWidth<0.0181 then -- sStorey="3" -- elseif BtlHasReinforce() then -- sStorey="2" -- elseif BtlIsIBeam() then -- sStorey="1" -- end sMat="Solid-Wood" if BtlIsIBeam() then sMat="I-Beams" end local sPackage=BtlGetPlankPackage() s = string.format( "[PART]\n" .. "SINGLEMEMBERNUMBER: %d\n" .. "ASSEMBLYNUMBER: \"\"\n" .. "ORDERNUMBER: %d\n" .. "DESIGNATION: \"%s\"\n" .. "ANNOTATION: \"%s\"\n" .. "STOREY: \"%s\"\n" .. "GROUP: \"%s\"\n" .. "STOREYTYPE: WALL\n" .. "ELEMENTNUMBER: \"\"\n" .. "LAYER: 0\n" .. "MODULENUMBER: \"\"\n" .. "PACKAGE: \"%s\"\n" .. "MATERIAL: \"%s\"\n" .. "TIMBERGRADE: \"\"\n" .. "QUALITYGRADE: \"\"\n" .. "COUNT: %d\n" .. "LENGTH: %08.0f\n" .. "HEIGHT: %08.0f\n" .. "WIDTH: %08.0f\n" .. "PLANINGLENGTH: 00000000\n" .. "STARTOFFSET: %08.0f\n" .. "ENDOFFSET: %08.0f\n", --"UID: %d\n" , gnBtlPieceNum, gnBtlPieceNum, ToBtl("(" .. sPart .. BtlGetIdPostStr() .. ")"), unidPlank, -- ANNOTATION sStorey, ToBtl(sPieceNum), sPackage, sMat, nSimilarCount, gnCurrTotLen*10000.0, x*10000.0, y*10000.0, gnBtlStartEndOffset*10000.0, gnBtlStartEndOffset*10000.0 ) --gnUid ) gsCncText=s if gbBtlWriteUid then -- UID and TRANSFORMATION s="" ac_objectclose() for i=1,nSimilarCount do ac_objectopen(tblSimilarPos[i].guid) s=s .. string.format("UID: %d\n%s\n", gnUid, BtlCreatePosStr("TRANSFORMATION", af_request("plankinfo"), gnBtlPlateSide)) ac_objectclose() tblSimilarPos[i].btluid=gnUid gnUid=gnUid+1 end gsCncText=gsCncText .. s ac_objectopen(unidPlank) else gnUid=gnUid+nSimilarCount end -- 12/2015: To avoid reinforcement markings intersecting the grooves (outside the plank) gtblGroInfos=BtlMakeGroInfoTbl() -- Frame plank has all machinings in single table including beginning and ending shapes -- # Machinings local nSide, sSide, nSortPos local bBegCut, bEndCut bBegCut=false bEndCut=false n=ac_objectget("iMc" ,-1) for i=1,n do nType=NormalizeType(ac_objectget("iMc", i, 1)) if nType==EMcFrAngledBegOld then gbAnyCnc=true bBegCut=true nAngleTop=ac_objectget("iMc", i, 2)*PI/180 nAngleSide=ac_objectget("iMc", i, 3)*PI/180 nStraightTop=ac_objectget("iMc", i, 4) nStraightSide=ac_objectget("iMc", i, 5) -- Konversio hirren tyyliin if nAngleSide<0 then nAngleSide=PI/2+nAngleSide else nAngleSide=-PI/2+nAngleSide end -- P��lt� vain konversio hundegger-tyyliin if nAngleTop<0 then nAngleTop=-PI/2-nAngleTop else nAngleTop=PI/2-nAngleTop end BtlAddBegAngledOld( sPieceNum, nAngleSide, nStraightSide, nAngleTop, nStraightTop ) elseif nType==EMcFrAngledBeg or nType==EMcFrAngledEnd then local isBeg isBeg=(nType==EMcFrAngledBeg) nAngleTop=ac_objectget("iMc", i, 2)*PI/180 nAngleSide=ac_objectget("iMc", i, 3)*PI/180 -- Bevel x=ac_objectget("iMc", i, 4) y=ac_objectget("iMc", i, 5) z=ac_objectget("iMc", i, 6) isBeg=BtlAddBegEndAngled( sPieceNum, nAngleTop, nAngleSide, x, y, z, isBeg, i ) if isBeg then bBegCut=true else bEndCut=true end if math.abs(nAngleTop-90)>0.1 or math.abs(nAngleSide-90)>0.1 then gbAnyCnc=true end elseif nType==EMcFrAngledBegTenon or nType==EMcFrAngledEndTenon then --! iMc[mindex][2], uihelp_length1: tapin pituus --! iMc[mindex][3], uihelp_length2: reunus yl� --! iMc[mindex][4], uihelp_length3: reunus ala --! iMc[mindex][5], uihelp_length4: tappi: reunus sivut, lohari: tapin leveys --! iMc[mindex][6], uihelp_length5: py�ristyss�de --! iMc[mindex][7], uihelp_angle1: kulma p��lt� --! iMc[mindex][8], uihelp_angle2: kallistuskulma --! iMc[mindex][9], uihelp_str3: Liitoksen tyyppi 1=Tappi, 2=Lohari --! iMc[mindex][10], uihelp_length6: Pohjav�lys --! iMc[mindex][11], uihelp_length7: Sivuv�lys --! iMc[mindex][12], uihelp_angle3: (Lohenpyrst�n) tapin kulma laajenus alhaalta yl�s gbAnyCnc=true bDove=false if ac_objectget("iMc", i, 9)==2 then bDove=true end tenonLen=ac_objectget("iMc", i, 2) tenonWidth=ac_objectget("iMc", i, 5) borderBot=ac_objectget("iMc", i, 4) tenonHeight=gnCurrHeight-ac_objectget("iMc", i, 3)-borderBot if not bDove then tenonWidth=gnCurrWidth-2*tenonWidth else -- If dovetail goes to top, set height to zero (empty=full dove male) if borderBot+tenonHeight+0.001>gnCurrHeight then tenonHeight=0 end end nAngleTopDeg=ac_objectget("iMc", i, 7) nAngleSideDeg=ac_objectget("iMc", i, 8) -- P��lt� vain konversio hundegger-tyyliin -- if nAngleTopDeg<0 then -- nAngleTopDeg=-90-nAngleTopDeg -- else -- nAngleTopDeg=90-nAngleTopDeg -- end -- 3=alapuoli nBtlSide=BtlGetLogBegBvnSide(3) if nType==EMcFrAngledEndTenon then nBtlSide=BtlGetLogEndBvnSide(3) nAngleSideDeg=-nAngleSideDeg nAngleTopDeg=-nAngleTopDeg end BtlAddBegEndTenon( sPieceNum, nBtlSide, bDove, tenonLen, tenonWidth, tenonHeight, borderBot, nAngleSideDeg, nAngleTopDeg, ac_objectget("iMc", i, 6), ac_objectget("iMc", i, 12) ) elseif nType==EMcVCutBeg or nType==EMcVCutEnd then --double dType; // 1, EMcVCutBeg/EMcVCutEnd --double dSideOneBased; // 2, 1-4 --double dX; // 3, distance from end to furthest point (usually zero) --double dY; // 4, Y-coordinate for the face from face mid line. pos to right in watching dir, neg to left --double dAngleDegR; // 5, angle at right side of the ref side --double dBevelDegR; // 6 --double dAngleDegL; // 7, Left side --double dBevelDegL; // 8 -- --// GDL generation set by AF --double dDirX; // 9, mid line vector relative to the plank x --double dDirY; // 10 --double dDirZ; // 11 --double dCutAngleDegR; // 12 Rotation for the cut, 0=no cut, 179=max --double dCutAngleDegL; // 13 -- local isBeg, nSide, angleDegR, bevelDegR, angleDegL, bevelDegL if nType==EMcVCutBeg then --gbBegCut=true isBeg=true else --gbEndCut=true isBeg=false end nSide =ac_objectget("iMc", i, 2) x =ac_objectget("iMc", i, 3) y =ac_objectget("iMc", i, 4) angleDegR =ac_objectget("iMc", i, 5) bevelDegR =ac_objectget("iMc", i, 6) angleDegL =ac_objectget("iMc", i, 7) bevelDegL =ac_objectget("iMc", i, 8) BtlAddBegEndVCut( sPieceNum, isBeg, nSide, x, y, angleDegR, bevelDegR, angleDegL, bevelDegL ) elseif nType==EMcFrBegHiddenShoe or nType==EMcFrEndHiddenShoe then --! iMc[mindex][2], uihelp_length1: kolouksen leveys --! iMc[mindex][3], uihelp_length2: kolouksen korkeus --! iMc[mindex][4], uihelp_length3: kolouksen syvyys --! iMc[mindex][5], uihelp_length4: uran syvyys (ura on yht� korkea kuin kolous) --! iMc[mindex][6], uihelp_length5: uran leveys --! iMc[mindex][7], uihelp_length6: alin poraus yKeski --! iMc[mindex][8], uihelp_length7: porausten et�isyys p��st� (keskipiste) --! iMc[mindex][9], uihelp_length8: poran halkaisija --! iMc[mindex][10], uihelp_length9: porien v�listys pystysuunnassa --! iMc[mindex][11], uihelp_count1: porausten lukum��r� local isBeg, holeWidth, holeHeight, holeDepth, groDepth, groWidth, drillLowest, drillFromEnd, drillSize, drillSpacing, drillCount gbAnyCnc=true isBeg=true if nType==EMcFrEndHiddenShoe then isBeg=false end holeWidth =ac_objectget("iMc", i, 2) holeHeight =ac_objectget("iMc", i, 3) holeDepth =ac_objectget("iMc", i, 4) groDepth =ac_objectget("iMc", i, 5) groWidth =ac_objectget("iMc", i, 6) drillLowest =ac_objectget("iMc", i, 7) drillFromEnd=ac_objectget("iMc", i, 8) drillSize =ac_objectget("iMc", i, 9) drillSpacing=ac_objectget("iMc", i, 10) drillCount =ac_objectget("iMc", i, 11) BtlAddHiddenShoe( isBeg, sPieceNum, holeWidth, holeHeight, holeDepth, groDepth, groWidth, drillLowest, drillFromEnd, drillSize, drillSpacing, drillCount ) elseif nType==EMcFrAngledEndOld then bEndCut=true gbAnyCnc=true nAngleTop=ac_objectget("iMc", i, 2)*PI/180 nAngleSide=ac_objectget("iMc", i, 3)*PI/180 nStraightTop=ac_objectget("iMc", i, 4) nStraightSide=ac_objectget("iMc", i, 5) -- Konversio hirren tyyliin if nAngleSide<0 then nAngleSide=-PI/2-nAngleSide else nAngleSide=PI/2-nAngleSide end -- P��lt� puolen vaihto ja konversio hundegger-tyyliin nAngleTop=-nAngleTop if nAngleTop<0 then nAngleTop=-PI/2-nAngleTop else nAngleTop=PI/2-nAngleTop end --toLog( string.format("nAngleTop=%f", nAngleTop) ) BtlAddEndAngledOld( sPieceNum, nAngleSide, nStraightSide, nAngleTop, nStraightTop ) elseif nType==EMcFrOpening then elseif nType==EMcFrGroove then gbAnyCnc=true x1 =ac_objectget("iMc", i, 2) y1 =ac_objectget("iMc", i, 3) x2 =ac_objectget("iMc", i, 4) y2 =ac_objectget("iMc", i, 5) groDepth=ac_objectget("iMc", i, 6) groWidth=ac_objectget("iMc", i, 7) groAngle=ac_objectget("iMc", i, 8)*PI/180.0 groFace =ac_objectget("iMc", i, 9) groAngleLen=ac_objectget("iMc", i, 10)*PI/180.0 groType =ac_objectget("iMc", i, 11) gtblGros[groFace]=gtblGros[groFace]+1 BtlAddGro(sPieceNum, x1, y1, x2, y2, groDepth, groWidth, groAngle, groFace, groAngleLen, groType) elseif nType==EMcFrDrill then gbAnyCnc=true x1 =ac_objectget("iMc", i, 2) y1 =ac_objectget("iMc", i, 3) groWidth=ac_objectget("iMc", i, 4) groDepth=ac_objectget("iMc", i, 5) groFace =ac_objectget("iMc", i, 6) BtlAddDrill(sPieceNum, x1, y1, groWidth, groDepth, groFace) elseif nType==EMcFrMarking then gbAnyCnc=true x1 =ac_objectget("iMc", i, 2) y1 =ac_objectget("iMc", i, 3) x2 =ac_objectget("iMc", i, 4) y2 =ac_objectget("iMc", i, 5) xPosText=ac_objectget("iMc", i, 6) yPosText=ac_objectget("iMc", i, 7) fontSize=ac_objectget("iMc", i, 8) groFace =ac_objectget("iMc", i, 9) textOff =ac_objectget("iMc", i, 10) nParallel=BitTest(ac_objectget("iMc", i, 11), 1) bLine=true if BitTest(ac_objectget("iMc", i, 11), 2)==1 then bLine=false end gtblMarkings[groFace]=gtblMarkings[groFace]+1 s=ac_objectget("iMcStr", i, 1) if math.abs(textOff)<0.001 or s=="" then -- No text offset BtlAddMarking(sPieceNum, groFace, x1, y1, x2, y2, xPosText, yPosText, fontSize, 0, s, bLine, nParallel) s="" elseif bLine then -- Only line BtlAddMarking(sPieceNum, groFace, x1, y1, x2, y2, xPosText, yPosText, fontSize, 0, "", bLine, nParallel) end if s~="" then -- Adjust the line with textOff and only text here dx=x2-x1 dy=y2-y1 z=math.sqrt(dx*dx+dy*dy) if z>0.001 then dx=dx/z dy=dy/z dx,dy=dy,-dx -- To right x1=x1+dx*textOff y1=y1+dy*textOff x2=x2+dx*textOff y2=y2+dy*textOff end BtlAddMarking(sPieceNum, groFace, x1, y1, x2, y2, xPosText, yPosText, fontSize, 0, s, false, nParallel) end elseif nType==EMcFrReinforce then gbAnyCnc=true x1 =ac_objectget("iMc", i, 2) y1 =ac_objectget("iMc", i, 3) dx =ac_objectget("iMc", i, 4) dy =ac_objectget("iMc", i, 5) groFace =ac_objectget("iMc", i, 7) xPosText=2 yPosText=2 if groFace==1 or groFace==3 then fontSize=gnCurrWidth else fontSize=gnProfHeight-gnBtlYoffset end fontSize=fontSize*50 -- 50% and to cm if dx<0.0005 then dx=gnCurrTotLen x1=gnCurrTotLen*0.5 end textOff =0 s=ac_objectget("iMcStr", i, 1) if s=="" then s=string.format("%.0f x %.0f", dx*1000, dy*1000) end s="" -- No text BtlMarkReinforcement( sPieceNum, groFace, x1, y1, dx, dy, xPosText, yPosText, fontSize, textOff, s ) if ac_objectget("iMc", i, 8)~=0 then -- Both sides if groFace==1 then groFace=3 elseif groFace==2 then groFace=4 elseif groFace==3 then groFace=1 elseif groFace==4 then groFace=2 elseif groFace==5 then groFace=6 elseif groFace==6 then groFace=5 end BtlMarkReinforcement( sPieceNum, groFace, x1, y1, dx, dy, xPosText, yPosText, fontSize, textOff, s ) end local xmin=x1-dx*0.5 local xmax=x1+dx*0.5 if nFirstStopMark==nil then nFirstStopMark=xmin y1Stop=y1-dy*0.5 y2Stop=y1Stop+dy elseif xminnLastStopMark then nLastStopMark=xmax end elseif nType==EMcFrNailGroup then if false then local col, row, cols, rows, anglex, angley gbAnyCnc=true x1 =ac_objectget("iMc", i, 2) y1 =ac_objectget("iMc", i, 3) cols =ac_objectget("iMc", i, 4) rows =ac_objectget("iMc", i, 5) anglex =ac_objectget("iMc", i, 6)*PI/180 angley =ac_objectget("iMc", i, 7)*PI/180 dx =ac_objectget("iMc", i, 8) dy =ac_objectget("iMc", i, 9) groFace =ac_objectget("iMc", i, 11) if groFace~=gnBtlBestPrintSide then if groFace==2 and gnBtlBestPrintSide==4 then groFace=4 elseif groFace==4 and gnBtlBestPrintSide==2 then groFace=2 end end for col=1,cols do for row=1,rows do x=x1 + (col-1)*dx*math.cos(anglex) + (row-1)*dy*math.cos(angley) y=y1 + (col-1)*dx*math.sin(anglex) + (row-1)*dy*math.sin(angley) BtlAddMarking(sPieceNum, groFace, x, y-0.02, x, y+0.02, 2, 2, 0, 0, "", true, 0, false, false) BtlAddMarking(sPieceNum, groFace, x-0.02, y, x+0.02, y, 2, 2, 0, 0, "", true, 0, false, false) end end end elseif nType==EMcFrNailLine then -- NOP here elseif nType==EMcFrSaw then -- Lengthwise sawing gbAnyCnc=true logFace =ac_objectget("iMc", i, 2) xStart =ac_objectget("iMc", i, 3) -- +begExtra dWidth =ac_objectget("iMc", i, 4) dDepthTop =ac_objectget("iMc", i, 5) dYoffTop =ac_objectget("iMc", i, 6) dAngleY2Deg =ac_objectget("iMc", i, 7) dAngleY1Deg =ac_objectget("iMc", i, 8) BtlAddSaw( sPieceNum, logFace, xStart, dWidth, dDepthTop, dYoffTop, dAngleY2Deg, dAngleY1Deg ) elseif nType==EMcFrTenonSide then --! ### 400: Tappiliitos tai lohari kylkeen --! iMc[mindex][2], uihelp_length1: keskipiste kapulan alusta --! iMc[mindex][3], uihelp_length2: korkeusasema kapulan alareunasta --! iMc[mindex][4], uihelp_length3: tapin pituus --! iMc[mindex][5], uihelp_length4: kolon leveys --! iMc[mindex][6], uihelp_length5: kolon korkeus --! iMc[mindex][7], uihelp_length6: Pohjav�lys --! iMc[mindex][8], uihelp_length7: Sivuv�lys --! iMc[mindex][9], uihelp_length8: py�ristyss�de --! iMc[mindex][10], uihelp_puoli: 1...4 --! iMc[mindex][11], uihelp_str3: Liitoksen tyyppi 1=Tappi, 2=Lohari --! iMc[mindex][12], uihelp_angle1: (Lohenpyrst�n) tapin kulma laajenus alhaalta yl�s local logFace, str, bDove, midPos, borderBot, tenonLen, tenonWidth, tenonHeight, sideGap, bottomGap, angleDove, roundingR gbAnyCnc=true bDove=false if ac_objectget("iMc", i, 11)==2 then bDove=true end midPos =ac_objectget("iMc", i, 2) borderBot =ac_objectget("iMc", i, 3) tenonLen =ac_objectget("iMc", i, 4) tenonWidth =ac_objectget("iMc", i, 5) tenonHeight =ac_objectget("iMc", i, 6) sideGap =ac_objectget("iMc", i, 7) bottomGap =ac_objectget("iMc", i, 8) roundingR =ac_objectget("iMc", i, 9) logFace =ac_objectget("iMc", i, 10) angleDove =ac_objectget("iMc", i, 12) BtlAddSideJoint( sPieceNum, logFace, bDove, midPos, borderBot, tenonLen, tenonWidth, tenonHeight, sideGap, bottomGap, angleDove, roundingR ) elseif nType==EMcFrBalkJoint then --! ### 401: Palkin kaulus --! iMc[mindex][2], uihelp_length1: keskipiste kapulan alusta --! iMc[mindex][3], uihelp_length2: kauluksen leveys --! iMc[mindex][4], uihelp_length3: kolous yl� --! iMc[mindex][5], uihelp_length4: kolous ala --! iMc[mindex][6], uihelp_length5: kolous sivut gbAnyCnc=true midPos =ac_objectget("iMc", i, 2) width =ac_objectget("iMc", i, 3) depthTop =ac_objectget("iMc", i, 4) depthBot =ac_objectget("iMc", i, 5) depthSides =ac_objectget("iMc", i, 6) BtlAddBalkMid(sPieceNum, midPos, width, depthTop, depthBot, depthSides) elseif nType==EMcFrBalkShoe then -- NOP on purpose elseif nType~=0 then AddCncErr( ac_objectget("iMc", i, 2), string.format("UNSUPPORTED MACHINING %d", nType) ) end end -- Reverted 12/2014 back to have these only if markings if BtlIsIBeam() and false then -- We want to have markings if y1Stop==nil then y1Stop=0 y2Stop=gnCurrHeight end if nFirstStopMark==nil then nFirstStopMark=gnCurrTotLen end if nLastStopMark==nil then nLastStopMark=0 end end if gbBtlMarkReinforce then if nFirstStopMark~=nil then -- Stop mark if any reinforcements and not at begin if nFirstStopMark>0.002 then BtlAddMarking(sPieceNum, 2, 0.001, y1Stop, 0.001, y2Stop, 0, 0, 0, 0, "", true, 0, true) BtlAddMarking(sPieceNum, 4, 0.001, y1Stop, 0.001, y2Stop, 0, 0, 0, 0, "", true, 0, true) end end if nLastStopMark~=nil then -- Stop mark if any reinforcements and not at end if nLastStopMark")) sMark=ac_environment ("strreplace", sMark, "#projid#", ac_environment("parsetext", "")) sMark=ac_environment ("strreplace", sMark, "#length#", ToBtl(ac_environment("ntos", gnCurrTotLen, "length", "dim"))) sMark=ac_environment ("strreplace", sMark, "#lengthmm#", string.format("%0.0f", gnCurrTotLen*1000)) xMid=gnCurrTotLen*0.5 if gnBtlPartIdFromEnd>=0 then if xMid-gnBtlPartIdFromEnd then xMid=-gnBtlPartIdFromEnd end end yTop=gnCurrHeight if BtlIsIBeam() or gnCurrHeight>gnBtlPartIdTopFromBot then yTop=gnBtlPartIdTopFromBot -- For I-Beams to the lower part (also all) if yTop>gnCurrHeight then yTop=gnCurrHeight end end -- Adjust so that does not intersect with grooves and markings width=string.len(sMark)*0.010 -- 10 mm per character width=width+0.040 x1=xMid-width*0.5 x1=af_request("mc_findtextpos", gnBtlBestPrintSide, x1, width) xMid=x1+width*0.5 BtlAddMarking(sPieceNum, gnBtlBestPrintSide, xMid, 0, xMid, yTop, 2, 2, 0, 0, sMark, false, 0) end -- From table to text if gTblCnc~=nil then -- Sort the table and create full text table.sort(gTblCnc, function (n1, n2) if math.abs(n1.pos-n2.pos)>EPS then return n1.pos < n2.pos -- compare the sort key end return n1.count < n2.count -- Keep the order if at same pos end) local tblBeg, tblMid, tblEnd, cncindex tblBeg=BtlProfPolyToEdges("iMatProfileXzBeg", gnCurrHeight) tblMid=BtlProfPolyToEdges("iMatProfileXzMid", gnCurrHeight) tblEnd=BtlProfPolyToEdges("iMatProfileXzEnd", gnCurrHeight) if tblEnd then BtlProfPolyToEnd(tblEnd, gnCurrTotLen) end cncindex=1 if tblBeg~=nil then -- Profile at begin, find position for i,v in ipairs(gTblCnc) do if v.pos>0 then cncindex=i break end end cncindex=cncindex+BtlInsertProfTbl(tblBeg, 0, false, gnCurrHeight, gTblCnc, cncindex) end if tblMid~=nil then -- Profile at mid local x1, x2 x1=0 if tblBeg then x1=tblBeg.box.x2 end x2=gnCurrTotLen if tblEnd then x2=tblEnd.box.x1 end while cncindex<=#gTblCnc do v=gTblCnc[cncindex] if v.pos>x1 then break end cncindex=cncindex+1 end cncindex=cncindex+BtlInsertProfTblMid(tblMid, gnCurrHeight, gTblCnc, cncindex, x1, x2) end if tblEnd~=nil then -- Profile at end while cncindex<=#gTblCnc do v=gTblCnc[cncindex] if v.pos>gnCurrTotLen-tblEnd.box.x2 then break end cncindex=cncindex+1 end cncindex=cncindex+BtlInsertProfTbl(tblEnd, 0, true, gnCurrHeight, gTblCnc, cncindex) end for i,v in ipairs(gTblCnc) do gsCncText=gsCncText .. v.text .. string.format("\nPROCESSIDENT: %d\n", i) end end return 1 end -- ### Lengthwise profile BEG -- Converts object's table into lines and arcs. Contour goes ccw/right hand side outside -- Returns: nil=no definition, otherwise structure having fields: -- box, structure -- x1,x2,y1,y2 Polygon bound box (x1,y1 always 0) -- hasbot Is bottom other than a line covering x1 -> x2 -- hastop Is top other than a line covering x1 -> x2 -- edges, 1-based table having fields: -- x1,y1 Begin pt -- x2,y2 End point -- isarc true=is arc and following set, nil=line -- midx,midy Arc's center point -- anglebegrad Angle at starting point -- anglelenrad Angle length function BtlProfPolyToEdges(parName, faceHeight) local n, i, v, prevx, prevy, midx, midy, x2, y2, status, dest local tbl, tblBox, tblEdges n=ac_objectget(parName,-1) if n<9 then return nil end x2=0 y2=0 i=1 prevx=nil midx=nil dest=0 tblEdges={} while i+2<=n do x=ac_objectget(parName, i) y=ac_objectget(parName, i+1) status=ac_objectget(parName, i+2) if status<100 then -- Includes end point if x>x2 then x2=x end if y>y2 then y2=y end if prevx and (math.abs(x-prevx)>EPS or math.abs(y-prevy)>EPS) then -- New edge tbl={} tbl.x1=prevx tbl.y1=prevy tbl.x2=x tbl.y2=y dest=dest+1 tblEdges[dest]=tbl end if status<0 then break end prevx=x prevy=y midx=nil elseif status>=900 and status<1000 then -- Circle center midx=x midy=y elseif status>=4000 and status<4100 then -- Arc/full circle, y=angledeg, if angle==360 then x=radius if math.abs(y)>359 then AddCncErr(0, string.format("Unsupported full circle in table %s", parName)) else -- Arc local a, r, dx, dy --ac_environment("tolog", string.format("prevx=%f prevy=%f", prevx, prevy)) tbl={} dy=prevy-midy dx=prevx-midx a=math.atan2(dy, dx) r=math.sqrt(dx*dx + dy*dy) tbl.anglebegrad=a a=a+y*PI/180 tbl.x1=prevx tbl.y1=prevy tbl.x2=midx+math.cos(a)*r tbl.y2=midy+math.sin(a)*r tbl.isarc=true tbl.midx=midx tbl.midy=midy tbl.anglelenrad=y*PI/180 dest=dest+1 tblEdges[dest]=tbl prevx=tbl.x2 prevy=tbl.y2 if tbl.x2>x2 then x2=tbl.x2 end if tbl.y2>y2 then y2=tbl.y2 end end else AddCncErr(0, string.format("Unsupported length shape code %d in table %s", status, parName)) end i=i+3 end tblBox={} tblBox.x1=0 tblBox.y1=0 tblBox.x2=x2 tblBox.y2=y2 tblBox.hasbot=true tblBox.hastop=true -- Check if straight top or bottom for i,v in ipairs(tblEdges) do if not v.isarc and math.abs(v.y1-v.y2)x2-EPS or v.x2x2-EPS) then if v.y1faceHeight-EPS then tblBox.hastop=false end end end tbl={} tbl.box=tblBox tbl.edges=tblEdges --DumpTbl( tbl.edges ) return tbl end -- Swaps coordinates to the plank's end and swaps direction of the polygon (to keep ccw) function BtlProfPolyToEnd(tblEnd, plankLen) local i, v, n for i,v in ipairs(tblEnd.edges) do v.x1,v.y1,v.x2,v.y2=plankLen-v.x2, v.y2, plankLen-v.x1, v.y1 -- Swap coords if v.isarc then v.midx=plankLen-v.midx v.anglebegrad=math.atan2(v.y1-v.midy, v.x1-v.midx) -- Not changing: v.anglelenrad end end -- Need to swap order also n=#tblEnd.edges for i=1,math.floor(n/2) do v=tblEnd.edges[i] tblEnd.edges[i]=tblEnd.edges[n+1-i] tblEnd.edges[n+1-i]=v end tblEnd.box.x1,tblEnd.box.x2=plankLen-tblEnd.box.x2, plankLen-tblEnd.box.x1 end function BtlIsPlankContour(edge, box, faceHeight) if math.abs(edge.y1-edge.y2)faceHeight-EPS then return true end end if math.abs(edge.x1-edge.x2)box.x2 then return true end end -- Not at polygon/plank contour return false end -- tblPoly From BtlProfPolyToEdges -- limitTopBot -1=bottom only, 0=all, 1=top only -- tblCnc cnc-table -- cncindex where to insert the mcs, also fist PROCESSIDENT: x -- x2clip nil=no clipping, value=where to clip the right hand side. Edits polygon if clipping occurs. -- Returns number of machinings added function BtlInsertProfTbl(tblPoly, limitTopBot, findBot, faceHeight, tblCnc, cncindex, x2clip) local i, n, v, i1, cnc, cncindexorg cncindexorg=cncindex -- Find first item for top side (ccw) if begin or bottom if end i1=nil for i,v in ipairs(tblPoly.edges) do if findBot==false then if v.x1+EPS>tblPoly.box.x2 then if i1==nil or v.y1>tblPoly.edges[i1].y1 then i1=i end end else if v.x1-EPS-EPS then begout=true end dist=ac_geo("linedist", x2clip, -1, x2clip, faceHeight+1, v.x2, v.y2) if dist>-EPS then endout=true end if begout and endout then skipEdge=true elseif begout or endout then local xx, yy xx,yy=ac_geo("linex", x2clip, -1, x2clip, faceHeight+1, v.x1, v.y1, v.x2, v.y2) if begout then v.x1=xx v.y1=yy else v.x2=xx v.y2=yy end end end if skipEdge==false then if v.isarc or not BtlIsPlankContour(v, tblPoly.box, faceHeight) then -- Add this edge addCount=addCount+1 tblAdd[addCount]=v v.edgeindex=i end end if limitTopBot<0 then if v.x2+EPS>tblPoly.box.x2 then break end elseif limitTopBot>0 then if v.x1-EPSn then i1=1 end end --DumpTbl(tblAdd) -- Add all collected edges local s, nextindex, isBeg, tbl isBeg=true for i,v in ipairs(tblAdd) do if isBeg then -- Add starting point segtype=10 -- Line contourType=20 -- Mill contour if isBeg then segType=0 -- Starting point contourType=0 -- Fee contour end s=string.format( "PROCESSKEY: 4-250-%d Free contour\n" .. "PROCESSPARAMETERS: P01:%08.0f P02:%08.0f P03:00000000 P05:00000000 P06:00000000 P07:00000000 P08:00000000 P09:%08d P13:00000000", --ADDED WHEN WRITTEN: "PROCESSIDENT: %d", BtlGetSide14(2), -- PROCESSKEY: 4-250-%d v.x1*10000, v.y1*10000, (cncindex+1)*10 -- P09 --cncindex -- PROCESSIDENT ) tbl={} tbl.text=s -- pos and count not needed table.insert( tblCnc, cncindex, tbl ) cncindex=cncindex+1 end nextindex=cncindex+1 if i==#tblAdd or v.edgeindex+1~=tblAdd[i+1].edgeindex then -- Terminate path nextindex=0 end s=nil if v.isarc then -- Arc segment local arcx, arcy, a, r -- Calc point at middle of the arc a=v.anglebegrad + v.anglelenrad*0.5 arcx=v.x1-v.midx arcy=v.y1-v.midy r=math.sqrt(arcx*arcx + arcy*arcy) arcx=v.midx+math.cos(a)*r arcy=v.midy+math.sin(a)*r s=string.format( "PROCESSKEY: 4-250-%d Free contour\n" .. "PROCESSPARAMETERS: P01:%08.0f P02:%08.0f P03:00000000 P05:00000000 P06:00000000 P07:00000000 P08:00000020 P09:%08d P10:%08.0f P11:%08.0f P12:00000000 P13:00000020", --ADDED WHEN WRITTEN: "PROCESSIDENT: %d", BtlGetSide14(2), -- PROCESSKEY: 4-250-%d v.x2*10000, v.y2*10000, nextindex*10, -- P09 arcx*10000, arcy*10000 --cncindex -- PROCESSIDENT ) else -- Line segment s=string.format( "PROCESSKEY: 4-250-%d Free contour\n" .. "PROCESSPARAMETERS: P01:%08.0f P02:%08.0f P03:00000000 P05:00000000 P06:00000000 P07:00000000 P08:00000010 P09:%08d P13:00000020", --ADDED WHEN WRITTEN: "PROCESSIDENT: %d", BtlGetSide14(2), -- PROCESSKEY: 4-250-%d v.x2*10000, v.y2*10000, nextindex*10 -- P09 --cncindex -- PROCESSIDENT ) end if s then tbl={} tbl.text=s -- pos and count not needed table.insert( tblCnc, cncindex, tbl ) cncindex=cncindex+1 end isBeg=false if nextindex==0 then isBeg=true end end return cncindex-cncindexorg end -- Like BtlInsertProfTbl but takes x1,x2 and multiplies polygon on that area & handles top and bottom separately function BtlInsertProfTblMid(tblPoly, faceHeight, tblCnc, cncindex, x1, x2) local xoff, i, v, res, resTot, x2clip resTot=0 xoff=x1 while x1+0.001 Full path file name of current pln WITHOUT extension (.pln removed) -- Return values (multiple ret values): -- 1=listing file name WITH extension, ""=Just prompt with file dlg, starting with *=result contains full path name - do not prompt -- 2=file extension to be used in save as dialog function OnInit(strPlnFileName) local projNum = ac_environment("parsetext", "") if not SettingsDlg() then gbCancel=true return end BvnOnInit() sFileName=projNum .. ".btl" sExt="btl" return sFileName, sExt end function BtlDoFileName(strFileName, strAdd) local path, fname, ext path, fname, ext=SplitFileName(strFileName, strNum) return string.format("%s%s%s%s", path, fname, strAdd, ext) end -- Called to do all that needs to be done -- strFileName is full path name for the result file function OnSaveList(strFileName) -- Edit BVN-settings here gnBtlPlateSide=EBtlMale gnBtlLogDir=EBtlDirBeg -- For TRANSFORMATION gnOrigX,gnOrigY,gnOrigZ=ac_environment("userorigin") if gnBtlWriteOnlyCore==1 then -- Filter out anything related to elements but not into core layer local tblPlanksNew, nPlanksNew, elemType, tblElem local tblElemTypes -- key=guid, value: 0=not core, 1=is core tblElemTypes={} tblPlanksNew={} nPlanksNew=0 i=1 while true do v=gTblPlanks[i] if v==nil then break end i=i+1 ac_objectopen(v) tblRes=af_request("plankinfo", v) ac_objectclose() owner=tblRes.ownerelemguid if owner~=nil then -- Check that owner element type is core elemType=tblElemTypes[owner] if elemType==nil then -- Not known - open element local ke, ve, currType tblElem=af_request("elem_openparent", owner) for ke,ve in ipairs(tblElem.tblelems) do currType=0 if ve.type=="core" then currType=1 end tblElemTypes[ve.guid]=currType end elemType=tblElemTypes[owner] end if elemType==1 then -- Core, add it owner=nil end -- Leave owner set not to be included end if not owner then nPlanksNew=nPlanksNew+1 tblPlanksNew[nPlanksNew]=v end end gTblPlanks=tblPlanksNew end if gnBtlWriteSepFiles>0 then -- Choose file for each plank, planks are sorted by id local tblPlankFiles local ownerNone, planksNow, tblRes, owner -- Fields: numPlanks=number of collected planks, tblPlanks=plank list, sortkey ownerNone="$$$NO OWNER$$$" tblPlankFiles={} -- key=owner=element id or one of above i=1 while true do v=gTblPlanks[i] if v==nil then break end ac_objectopen(v) -- Related to any element? tblRes=af_request("plankinfo", v) owner=tblRes.ownerelemid if owner==nil then owner=ownerNone end planksNow=tblPlankFiles[owner] if planksNow==nil then planksNow={} planksNow.numPlanks=0 planksNow.tblPlanks={} planksNow.sortkey=owner planksNow.owner=owner tblPlankFiles[owner]=planksNow end planksNow.numPlanks=planksNow.numPlanks+1 planksNow.tblPlanks[planksNow.numPlanks]=v ac_objectclose(v) i=i+1 end local key, value, tblSorted, elemsCount tblSorted={} elemsCount=0 for key,value in pairs(tblPlankFiles) do elemsCount=elemsCount+1 tblSorted[elemsCount]=value end table.sort(tblSorted, function (n1, n2) return n1.sortkey < n2.sortkey end) for key,value in ipairs(tblSorted) do gnBtlPieceNum=nil gTblPlanks=value.tblPlanks owner=value.owner gnBtlPieceNum=nil gBtlForceId=nil gnUid=1 if owner==ownerNone then --Write without any addition to the file name BtlOnSaveListInt(strFileName, true) else fname=BtlDoFileName(strFileName, "_" .. owner) BtlOnSaveListInt(fname, true) end end else -- All into single file gnBtlPieceNum=nil gBtlForceId=nil gnUid=1 BtlOnSaveListInt(strFileName, true) end if false then -- Debug all feeding variations local dir, plate ac_msgbox( "TESTING, PLANK FED ALL WAYS" ) gbBvnForceUnid=true for dir=0,0 do gnBtlLogDir=dir for plate=0,3 do gnBtlPlateSide=plate gBtlForceId=string.format("%s gnBtlPlateSide=%d", gsCurrentLogId, plate) gtblUsedIds = {} BtlOnSaveListInt(strFileName, false) gBtlForceId=nil end end end end function BtlParseElemGroupSort(s) if string.match(s, "bottom_.*") then return "b" end if string.match(s, "top_.*") or string.match(s, "contourtilted") then return "c" end return "a" end function BtlPlankCmpElemGroupFull(p1, p2) local s1, s2 if p1.idp2.id then return 1 end if not gbBtlTopBottomFile then return 0 end s1=BtlParseElemGroupSort(p1.elemgroup) s2=BtlParseElemGroupSort(p2.elemgroup) if s1s2 then return 1 end return 0 end -- Called to do all that needs to be done -- strFileName is full path name for the result file function BtlOnSaveListInt(strFileName, bWriteHeader) local i, id, v local tblSortedPlanks local sTotPlank local sBotCnc, sBotNoCnc, sTopCnc, sTopNoCnc, sTopBotCnc local elemGroup, fileName -- Start writing sTotPlank="" sBotCnc="" sBotNoCnc="" sTopCnc="" sTopNoCnc="" sTopBotCnc="" -- If gbBtlTopBottomSingle tblSortedPlanks={} i=1 while true do v=gTblPlanks[i] if v==nil then break end ac_objectopen(v) tblSortedPlanks[i]={} tblSortedPlanks[i].guid=v tblSortedPlanks[i].id=ac_objectget("#id") tblSortedPlanks[i].elemgroup=ac_objectget("iElemGroup") ac_objectclose(v) i=i+1 end table.sort(tblSortedPlanks, function(p1, p2) return BtlPlankCmpElemGroupFull(p1,p2)<0 end) i=1 while true do if tblSortedPlanks[i]==nil then break end gsCurrentLogId=tblSortedPlanks[i].id if gbBtlTopBottomFile then CheckId(string.format("%s/%s", gsCurrentLogId, tblSortedPlanks[i].elemgroup) ) else CheckId(gsCurrentLogId) end -- Calculate how many with the same ID and write them at once local nSimilarCount, tblSimilarPos nSimilarCount=1 tblSimilarPos={} tblSimilarPos[nSimilarCount]={} tblSimilarPos[nSimilarCount].guid=tblSortedPlanks[i].guid while tblSortedPlanks[i+nSimilarCount]~=nil do if BtlPlankCmpElemGroupFull(tblSortedPlanks[i], tblSortedPlanks[i+nSimilarCount])~=0 then break end nSimilarCount=nSimilarCount+1 tblSimilarPos[nSimilarCount]={} tblSimilarPos[nSimilarCount].guid=tblSortedPlanks[i+nSimilarCount-1].guid end ac_objectopen(tblSortedPlanks[i].guid) elemGroup=ac_objectget("iElemGroup") BtlWritePlank( tblSortedPlanks[i].guid, nSimilarCount, tblSimilarPos ) -- Sets gsCncText and gbAnyCnc ac_objectclose(tblSortedPlanks[i].guid) if gbBtlTopBottomFile then if string.match(elemGroup, "top_.*") or string.match(elemGroup, "contourtilted") then -- Top piece if gbBtlTopBottomSingle then sTopBotCnc = sTopBotCnc .. gsCncText elseif gbAnyCnc then sTopCnc = sTopCnc .. gsCncText else sTopNoCnc = sTopNoCnc .. gsCncText end gsCncText=nil elseif string.match(elemGroup, "bottom_.*") then -- Top piece if gbBtlTopBottomSingle then sTopBotCnc = sTopBotCnc .. gsCncText elseif gbAnyCnc then sBotCnc = sBotCnc .. gsCncText else sBotNoCnc = sBotNoCnc .. gsCncText end gsCncText=nil end end if gsCncText then sTotPlank=sTotPlank .. gsCncText end i=i+nSimilarCount end gsCncText=nil if sTotPlank~="" then BtlWriteFile( bWriteHeader, strFileName, sTotPlank ) end fileName=BtlGetSpecialFileName( strFileName, "_bot_machining" ) os.remove(fileName) if sBotCnc~="" then BtlWriteFile( bWriteHeader, fileName, sBotCnc ) end fileName=BtlGetSpecialFileName( strFileName, "_bot_plain" ) os.remove(fileName) if sBotNoCnc~="" then BtlWriteFile( bWriteHeader, fileName, sBotNoCnc ) end fileName=BtlGetSpecialFileName( strFileName, "_top_machining" ) os.remove(fileName) if sTopCnc~="" then BtlWriteFile( bWriteHeader, fileName, sTopCnc ) end fileName=BtlGetSpecialFileName( strFileName, "_top_plain" ) os.remove(fileName) if sTopNoCnc~="" then BtlWriteFile( bWriteHeader, fileName, sTopNoCnc ) end fileName=BtlGetSpecialFileName( strFileName, "_topbot" ) os.remove(fileName) if sTopBotCnc~="" then BtlWriteFile( bWriteHeader, fileName, sTopBotCnc ) end end -- Returns path,file name and extension. nil=cannot find function SplitFileName(strPathName) local path, fname, ext local i, c, extIndex i=string.len(strPathName) extIndex=i while i>0 do c=string.sub(strPathName, i, i) if ext==nil and c=="." then ext=string.sub(strPathName, i, string.len(strPathName)) extIndex=i-1 elseif fname==nil and (c=="\\" or c=="/") then fname=string.sub(strPathName, i+1, extIndex) path=string.sub(strPathName, 1, i) break end i=i-1 end return path,fname,ext end function BtlGetSpecialFileName(strFileName, strAdd) local path, fname, ext path, fname, ext=SplitFileName(strFileName, strNum) return string.format("%s%s%s%s", path, fname, strAdd, ext) end function BtlWriteFile( bWriteHeader, strFileName, sTotPlank ) local hFile if bWriteHeader then hFile=io.open(strFileName, "wt") else hFile=io.open(strFileName, "at") end io.output( hFile ) if bWriteHeader then -- btl-header... local s local projNum = ac_environment("parsetext", "") local projName = ac_environment("parsetext", "") local custName = ac_environment("parsetext", "") s=string.format( "VERSION: \"BTL V10.6\"\n" .. "BUILD: \"10400\"\n" .. "EDITION: PREFABRICATION\n" .. "[GENERAL]\n" .. "PROJECTNUMBER: \"%s\"\n" .. "PROJECTNAME: \"%s\"\n" .. "PROJECTPART: \"\"\n" .. "LISTNAME: \"ARCHIFRAME\"\n" .. "CUSTOMER: \"%s\"\n" .. "ARCHITECT: \"\"\n" .. "EDITOR: \"\"\n" .. --"DELIVERYDATE: \"0:00:00\"\n" .. --"EXPORTDATE: \"2011-12-12\"\n" .. --"EXPORTTIME: \"14:54:11\"\n" .. "EXPORTRELEASE: \"ArchiFrame BTL 2012-06-01\"\n" .. "LANGUAGE: \"EN\"\n" .. "SCALEUNIT: 1\n" .. "PROCESSINGQUALITY: AUTOMATIC\n" .. "COMPUTERNAME: \"\"\n" .. "USER: \"\"\n" .. "SOURCEFILE: \"\"\n" .. "EXPORTFILE: \"\"\n" .. "RECESS: AUTOMATIC\n" .. "COMMENT: \"\"\n", ToBtl(projNum), ToBtl(projName), ToBtl(custName) ) io.write(s) -- ...btl-header end io.write( sTotPlank ) io.close( hFile ) end ----------------------------------------------------------------------------- -- BTL specific constants gnBtlStartEndOffset = 0.020 -- How much waste wood at beg & end of the piece in meters -- Feeding into machine gnBtlPlateSide=EBtlMale gnBtlLogDir=EBtlDirBeg gbBtlCalcFeed=true -- If set to true, plank is fed so that longer extent lies on table (overrides previous settings) gsBtlPartId="(#fullid)" -- Plank ID as marking machining, replaced: #fullid#, #projid# gnBtlBestPrintSide=4 -- Depends on the feeding direction: ID and nail marks are placed on this surface gnBtlMinGroWidth=0 -- The cutter diameter (keep middle line and make it wider if necessary), nil/0=no change gbBtlTopBottomFile=false -- Write top & bottom pieces to separate file? gbBtlTopBottomSingle=false -- Just single base_topbot.btl? gbBtlMarkReinforce=true -- Previous replaced with this 9/2019 gbBtlMoveGroFromEnd=false -- Move groove from end 1 mm inwards (faces 1-4 only) gnBtlWriteSepFiles=0 -- 0=All into single file, 1=split by owning element ID gnBtlWriteOnlyCore=0 -- 0=write all, 1=if part of element, write only core layer gbBtlWriteUid=true -- Added 4/2024: true=write UID and TRANSFORMATION, false=as before do not write