-- Weinmann & GranIT specific CNC-writer ArchiSolutions 8/2024 gScriptUtf8=1 -- We are fully utf --------------------------------------------------------------------------- -- Weinmann & GranIT SPECIFIC DEFINITIONS --> -- Element classifications (not used for now) EElemTypeWall=1 EElemTypeFloor=2 EElemTypeRoof=3 -- Destination cut saws EDestWeinmann=1 -- btl EDestTiger=2 -- btl but not used for now (not tested) EDestBvn=3 gnDestMachine=EDestWeinmann -- Not used (to be used if EDestTiger was supported) gtblDestNames = { } -- Part of file name gtblDestNames[1] = "" gtblDestNames[2] = "" gtblDestExts = { } -- File extensions gtblDestExts[1] = ".btl" gtblDestExts[2] = ".bvn" --------------------------------------------------------------------------- -- 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 EMcFrBeamFemale = 901 -- Ostlaft tak�s balk joint in log G2 etc EMcFrBalkShoe = 1000 gnSortPosLast=9999 -- If you want the machining to be the last one(s) gsNoElemGuid="!NO_ELEM-" -- Pseudo guid for planks not part of any element, floor number could be added after this. Exclamation mark to get it first in the sort order --------------------------------------------------------------------------- -- COMMON FUNCTIONS MOVED HERE -- From Bvn -- # Info field definitions (11/2017), same code shared in bvn&btl EInfoNone = 1 -- No text to this field EInfoFullIDPlusUsage= 2 -- Full ID (usage) EInfoShortID = 3 -- For example EW01-07 will give only 07 excluding wall etc id EInfoFullID = 4 -- For example EW01-07 will give full EW01-07 EInfoElementID = 5 -- The owning element's ID part (EW01) EInfoUsage = 6 -- Plank's usage EInfoMatIdFull = 7 -- Full material ID EInfoMatIdShort = 8 -- Material ID everything removed from last space bar EInfoGrade = 9 -- Find grade C24/C18/IMP from material type (any text after plank IDs last space bar) EInfoMatIdIfNotSize = 10 -- Add material ID if different from width x size EInfoPlankRole = 11 -- Role in element EInfoPlankRoleFin = 12 -- Role in element Finnish EInfoPlankRoleNor = 13 -- Role in element Norsk EInfoPlankRoleShort = 14 -- Stud=ST EInfoPlankRoleShortFin= 15 -- Tolppa=TO EInfoPlankRoleShortNor=16 -- Norsk short EInfoPackageOrElemID=17 -- iPackage or if it is empty, EInfoElementID -- Returns element ID, plank ID. For example for EW01-07 will return "EW01","07"" function SplitId(id, plankinfo) local elemid, s, pos s="-" -- This is the master plank not showing the short ID probably: ac_objectget("iShowIDSep") if plankinfo.type==4 then s="#" -- board end elemid=id -- Find element ID --pos=string.find(id, s, 1, true) -- No reverse find in Lua pos=string.len(id) while pos>=1 do if string.sub(id,pos,pos)==s then break end pos=pos-1 end elemid="" if pos>1 then elemid=string.sub(id, 1, pos-1) end id=string.sub(id, pos+1) return elemid, id end -- Splits material ID to two parts from last space. If no space, last part will be "" function SplitMatId() local elemid, s, id, grade, pos s=" " -- Separator id=ac_objectget("iMatId") pos=string.len(id) while pos>=1 do if string.sub(id,pos,pos)==s then break end pos=pos-1 end grade="" if pos>1 then grade=string.sub(id, pos+1) id=string.sub(id, 1, pos-1) end return id, grade end -- COMMON FUNCTION IN A FEW PLACES IN DATA-FOLDER, UPDATE ALL IF UPDATING ONE -- isHor Is it horizontal structure (false/nil=default) -- lang eng/swe/fin -- elemgroup Parameter iElemGroup function GetElemGroupName(isHor, elemgroup) local lang lang=af_request("aflang") if isHor then -- Horizontal structure if elemgroup==nil then elemgroup="" elseif string.match(elemgroup, "^top%a*") or string.match(elemgroup, "^2ndtop%a*") or string.match(elemgroup, "^contour_x.*") then elemgroup="Stud" if lang=="fin" then elemgroup="Tolppa" elseif lang=="swe" then elemgroup="Regel" end elseif string.match(elemgroup, "^bottom%a*") or string.match(elemgroup, "^2ndbottom%a*") then elemgroup="Stud" if lang=="fin" then elemgroup="Tolppa" elseif lang=="swe" then elemgroup="Regel" end elseif string.match(elemgroup, "^vertical_x%a*") or string.match(elemgroup, "^vertical_y%a*") or string.match(elemgroup, "^contourtilted_opening%a*") then elemgroup="Door/win" if lang=="fin" then elemgroup="Ikk/ovi" elseif lang=="swe" then elemgroup="Avv�xling" end elseif string.match(elemgroup, "^vertical%a*") then elemgroup="Hor" if lang=="fin" then elemgroup="Vaaka" end elseif string.match(elemgroup, "^contourtilted%a*") then elemgroup="Angled" if lang=="fin" then elemgroup="Vinojuoksu" elseif lang=="swe" then elemgroup="Regel" end elseif string.match(elemgroup, "^balk.*") then elemgroup="Beam" if lang=="fin" then elemgroup="Palkki" elseif lang=="swe" then elemgroup="B�rlina" end elseif string.match(elemgroup, "^lintel.*") then elemgroup="W/D beam" if lang=="fin" then elemgroup="Aukkopalkki" elseif lang=="swe" then elemgroup="B�rplanka" end elseif string.match(elemgroup, "^reinforce.*") then elemgroup="Reinforcement" if lang=="fin" then elemgroup="Vahvike" elseif lang=="swe" then elemgroup="F�rst�rkning" end elseif string.match(elemgroup, "^nogging.*") then elemgroup="Nogging" if lang=="nor" then elemgroup="Kubbing" elseif lang=="fin" then elemgroup="Nurjahdustuki" elseif lang=="swe" then elemgroup="Kortling" end elseif elemgroup=="sideleft_spacing" or elemgroup=="sideright_spacing" then elemgroup="Side beams" if lang=="fin" then elemgroup="Sivupalkki" elseif lang=="swe" then elemgroup="�ndkortling" end else elemgroup="" -- Just leave it empty, earlier was "? (elemgroup)" end else -- Vertical structure if elemgroup==nil then elemgroup="" elseif string.match(elemgroup, "^top%a*") or string.match(elemgroup, "^2ndtop%a*") then elemgroup="Top plate" if lang=="fin" then elemgroup="Yl�juoksu" elseif lang=="swe" then elemgroup="HB" end elseif string.match(elemgroup, "^bottom%a*") or string.match(elemgroup, "^2ndbottom%a*") then elemgroup="Bottom plate" if lang=="fin" then elemgroup="Alajuoksu" elseif lang=="swe" then elemgroup="Syll" end elseif string.match(elemgroup, "^contour_x.*") then elemgroup="Top/bottom plate" if lang=="fin" then elemgroup="Yl�/alajuoksu" elseif lang=="swe" then elemgroup="HB/Syll" end elseif string.match(elemgroup, "^vertical_x%a*") or string.match(elemgroup, "^contourtilted_opening%a*") then elemgroup="Win/door" if lang=="fin" then elemgroup="Ikk/ovi" elseif lang=="swe" then elemgroup="Avv�xling" end elseif string.match(elemgroup, "^vertical%a*") or string.match(elemgroup, "^contour_y.*") then elemgroup="Stud" if lang=="fin" then elemgroup="Tolppa" elseif lang=="swe" then elemgroup="Regel" end elseif string.match(elemgroup, "^contourtilted%a*") then elemgroup="Angled" if lang=="fin" then elemgroup="Vinojuoksu" elseif lang=="swe" then elemgroup="Regel" end elseif string.match(elemgroup, "^balktop.*") then elemgroup="Beam top" if lang=="fin" then elemgroup="Palkki yl�" elseif lang=="swe" then elemgroup="B�rlina topp" end elseif string.match(elemgroup, "^balkbot.*") then elemgroup="Beam bottom" if lang=="fin" then elemgroup="Palkki ala" elseif lang=="swe" then elemgroup="B�rlina botten" end elseif string.match(elemgroup, "^lintel.*") then elemgroup="W/D beam" if lang=="fin" then elemgroup="Aukkopalkki" elseif lang=="swe" then elemgroup="B�rplanka" end elseif string.match(elemgroup, "^nogging.*") then elemgroup="Nogging" if lang=="nor" then elemgroup="Kubbing" elseif lang=="fin" then elemgroup="Nurjahdustuki" elseif lang=="swe" then elemgroup="Kortling" end elseif elemgroup=="sideleft_spacing" or elemgroup=="sideright_spacing" then elemgroup="Side beams" if lang=="fin" then elemgroup="Sivupalkki" elseif lang=="swe" then elemgroup="�ndkortling" end else elemgroup="" -- Just leave it empty, earlier was "? (elemgroup)" end end return elemgroup end -- nInfo One of EInfoXxxx function GetInfoStr(nInfo, plankinfo) local s, s2 s="" if nInfo==EInfoFullIDPlusUsage then s=ac_objectget("iUsageId") if not s then s="" end if s~="" then s=string.format( "%s (%s)", gsCurrentLogId, s) else s=gsCurrentLogId end elseif nInfo==EInfoShortID then s2, s=SplitId(gsCurrentLogId, plankinfo) elseif nInfo==EInfoFullID then s=gsCurrentLogId elseif nInfo==EInfoElementID then s, s2=SplitId(gsCurrentLogId, plankinfo) elseif nInfo==EInfoPackageOrElemID then s=ac_objectget("iCncPackage") if s==nil or s=="" then s, s2=SplitId(gsCurrentLogId, plankinfo) end elseif nInfo==EInfoGrade then s2, s=SplitMatId() elseif nInfo==EInfoUsage then s=ac_objectget("iUsageId") elseif nInfo==EInfoMatIdFull then s=ac_objectget("iMatId") elseif nInfo==EInfoMatIdShort then s, s2=SplitMatId() elseif nInfo==EInfoMatIdIfNotSize then s=ac_objectget("iMatId") if s==string.format("%.0fx%.0f", gnCurrWidth*1000, gnCurrHeight*1000) then s="" end elseif nInfo==EInfoPlankRole then s=ac_objectget("iElemGroup") s=GetElemGroupName(false, s) elseif nInfo==EInfoPlankRoleShort then s=ac_objectget("iElemGroup") if s==nil then s="" elseif string.match(s, "^top%a*") or string.match(s, "^2ndtop%a*") then s="TP" elseif string.match(s, "^bottom%a*") or string.match(s, "^2ndbottom%a*") then s="BP" elseif string.match(s, "^vertical_x%a*") or string.match(s, "^contourtilted_opening%a*") then s="HO" elseif string.match(s, "^vertical%a*") then s="ST" elseif string.match(s, "^contourtilted%a*") then s="AN" elseif string.match(s, "^balk%a*") then s="BE" elseif string.match(s, "^lintel.*") then s="LI" else s="" end elseif nInfo==EInfoPlankRoleFin then s=ac_objectget("iElemGroup") if s==nil then s="" elseif string.match(s, "^top%a*") or string.match(s, "^2ndtop%a*") then s="Yl�juoksu" elseif string.match(s, "^bottom%a*") or string.match(s, "^2ndbottom%a*") then s="Alajuoksu" elseif string.match(s, "^vertical_x%a*") or string.match(s, "^contourtilted_opening%a*") then s="Ikk/ovi" elseif string.match(s, "^vertical%a*") then s="Tolppa" elseif string.match(s, "^contourtilted%a*") then s="Vinojuoksu" elseif string.match(s, "^balk%a*") then s="Palkki" elseif string.match(s, "^lintel.*") then s="Aukkopalkki" end elseif nInfo==EInfoPlankRoleShortFin then s=ac_objectget("iElemGroup") if s==nil then s="" elseif string.match(s, "^top%a*") or string.match(s, "^2ndtop%a*") then s="YJ" elseif string.match(s, "^bottom%a*") or string.match(s, "^2ndbottom%a*") then s="AJ" elseif string.match(s, "^vertical_x%a*") or string.match(s, "^contourtilted_opening%a*") then s="VA" elseif string.match(s, "^vertical%a*") then s="T" elseif string.match(s, "^contourtilted%a*") then s="VI" elseif string.match(s, "^balk%a*") then s="PA" elseif string.match(s, "^lintel.*") then s="AU" else s="" end elseif nInfo==EInfoPlankRoleNor then s=ac_objectget("iElemGroup") if s==nil then s="" elseif string.match(s, "^top%a*") or string.match(s, "^2ndtop%a*") then s="Toppsvill" elseif string.match(s, "^bottom%a*") or string.match(s, "^2ndbottom%a*") then s="Bunnsvill" elseif string.match(s, "^vertical_x%a*") or string.match(s, "^contourtilted_opening%a*") then s="Vindu/d�r" elseif string.match(s, "^vertical%a*") then s="Stender" elseif string.match(s, "^contourtilted%a*") then s="Vinkel" elseif string.match(s, "^balk%a*") then s="Bjelke" elseif string.match(s, "^lintel.*") then s="Overdekning" end elseif nInfo==EInfoPlankRoleShortNor then s=ac_objectget("iElemGroup") if s==nil then s="" elseif string.match(s, "^top%a*") or string.match(s, "^2ndtop%a*") then s="TS" elseif string.match(s, "^bottom%a*") or string.match(s, "^2ndbottom%a*") then s="BS" elseif string.match(s, "^vertical_x%a*") or string.match(s, "^contourtilted_opening%a*") then s="LH" elseif string.match(s, "^vertical%a*") then s="ST" elseif string.match(s, "^contourtilted%a*") then s="LH" elseif string.match(s, "^balk%a*") then s="BJ" elseif string.match(s, "^lintel.*") then s="OD" else s="" end end if s==nil then s="" end return s end -- Returns width,height function GetPlankSize() local width,height width=ac_objectget("iWidth") if width==nil or width==0.0 then width=ac_objectget("A") end height=ac_objectget("iHeight") if height==nil or height==0.0 then height=ac_objectget("B") end return width,height end --------------------------------------------------------------------------- -- ARCHIFRAME & ARCHILOGS COMMON FUNCTIONS function toLog(str) ac_environment("tolog", str) end -- Dumps given variable into string function DumpTblInt(o, nIndent) local s,k,v if type(o) == 'table' then s = '{\n' for i = 0, nIndent do s = s .. '\t' end for k,v in pairs(o) do if type(k) ~= 'number' then k = '"'..k..'"' end s = s .. '['..k..'] = ' .. DumpTblInt(v, nIndent + 1) .. ',\n' for i = 0, nIndent do s = s .. '\t' end end if string.len(s) > 1 then if string.sub(s, -1) == '\t' then s = string.sub(s, 1, -2) end end s=s .. '}' else s = "" for i = 0, nIndent do s = s .. '\t' end if type(o) == 'string' then s = '"' .. tostring(o) .. '"' elseif type(o) == 'number' then -- Less digits to be able to compare results s = '"' .. string.format("%0.4f", o) .. '"' else s = tostring(o) end end return s end -- Dumps a table to the log window with indentation. function DumpTbl(o) ac_environment("tolog", DumpTblInt(o, 0)) end -- Table copy values function deepcopy(orig) local orig_type = type(orig) local copy if orig_type == 'table' then copy = {} for orig_key, orig_value in next, orig, nil do copy[deepcopy(orig_key)] = deepcopy(orig_value) end setmetatable(copy, deepcopy(getmetatable(orig))) else -- number, string, boolean, etc copy = orig end return copy 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 -- 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 function GetSgn(d) if d<-EPS then return -1 end if d>EPS then return 1 end return 0 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 -- Finds key=val from string having \n as separator -- Returns: nil=not found, str=value for requested key function FindKeyVal(strData, keyFind) local i1,i2,s2,firstmsg firstmsg=true i1=1 while i1x2 then x2=x end if y>y2 then y2=y end return x1, y1, x2, y2 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 gbSkipCncErr=false -- Set to true to be silent -- Adds cnc error message to gTblCncErr function AddErrMsg(sLogId, nPos, sText, guidElem) if nPos==nil then error("AddCncErr/nPos") end if sText==nil then error("AddCncErr/sText") end if gnCncErrCount==0 then gTblCncErr={} end if not guidElem then guidElem=gsCurrentLogGuid end gnCncErrCount=gnCncErrCount+1 gTblCncErr[gnCncErrCount] = {} gTblCncErr[gnCncErrCount].guid=guidElem gTblCncErr[gnCncErrCount].logid=sLogId gTblCncErr[gnCncErrCount].pos=nPos gTblCncErr[gnCncErrCount].text=sText end -- Adds cnc error message to gTblCncErr function AddCncErr(nPos, sText) if not gbSkipCncErr then AddErrMsg(gsCurrentLogId, nPos, sText) end end gtblUsedIds = {} -- T�nne asetetaan ["id"]=1, ett� l�ydet��n samalla ID:ll� olevat hirret function CheckId( sId, bDontCheckIds ) if sId=="" then local guid guid=ac_objectget("#guid") if guid==nil then guid="" else guid=" GUID=" .. guid end AddCncErr( 0, string.format("EMPTY ID FOUND" .. guid) ) elseif gtblUsedIds[sId]~=null and not bDontCheckIds then AddCncErr( 0, string.format("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 -- Returns cnc_elemtype for the element: Either from composite's cnc_elemtype or from related cutlists, nil=not found function GetCncElemType(elemQuant) local cnc_elemtype if elemQuant.xmlutf8 then cnc_elemtype=string.match(elemQuant.xmlutf8, " cnc_elemtype=\"(.-)\"") end if elemQuant.tblguidcutlist then -- Check if there is info cnc_elemtype in the cut list(s) local i, v, i2, parName, found, par, eleminfo found=nil for i,v in ipairs(elemQuant.tblguidcutlist) do -- Check if cut list is text only eleminfo=ac_elemget(v) if eleminfo and eleminfo.header and eleminfo.header.typeID==6 then ac_objectopen(v) for i2=1,10 do parName=string.format("iInfoKey%d", i2) par=ac_objectget(parName) if par and par=="cnc_elemtype" then parName=string.format("iInfoValue%d", i2) par=ac_objectget(parName) if par and par~="" then found=par end end end ac_objectclose() if found then cnc_elemtype=found break end end end end return cnc_elemtype end --------------------------------------------------------------------------- -- ARCHIFRAME & ARCHILOGS GEOMETRY FUNCTIONS -- Returns VECTOR x,y,z transformed function MatTransformVec( x, y, z, tblTran ) local xnew, ynew, znew xnew=x*tblTran[1]+ y*tblTran[2]+ z*tblTran[3] ynew=x*tblTran[5]+ y*tblTran[6]+ z*tblTran[7] znew=x*tblTran[9]+ y*tblTran[10]+ z*tblTran[11] return xnew, ynew, znew end -- Returns point x,y,z transformed function MatTransformPt( x, y, z, tblTran ) return MatTransformVec( x+tblTran[4], y+tblTran[8], z+tblTran[12], tblTran ) end -- ### 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 function CaclDot3D(v1, v2) return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z end function CaclDot2D(v1, v2) return v1.x * v2.x + v1.y * v2.y 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 -- ### Sector and plane intersection -- in: -- ta,tb,tc,td Plane equation, a-c=normal, d=distance from zero -- x1, y1, z1 sector -- x2, y2, z2 sector -- out: -- hasX Was there an intersection -- x, y, z Point -- dist Distance from point x1,y1,z1 in line's dir (relative to the sector's length 0...1!) 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 -- To return correct dist: return true, x, y, z, dist*math.abs(denom) 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)0 then -- Straight at bottom side nStraightSide=nStraightSide-gnBvnYoffset end end if bMirrorY then nAngleSide=-nAngleSide end --if bMirrorY then --if nAngleSide<0 then --xSide=nStraightSide --nAngleSide=-nAngleSide --else --xSide=gnProfHeight-nStraightSide --nAngleSide=PI-nAngleSide --end --else xSide=nStraightSide if math.abs(nAngleSide) depth2=depth1/4 -- depth3 can probably be changed as wished, but let's keep it 10% of depth1 depth1=gnFullHeight*2.0/3.0 -- Always calculate values from full profile and depth2=depth1/4.0 depth3=0 -- depth1/10.0: Nope, let the log object's shorten (negative=extend) parameter define overhang if gnHalfing==EHalfingLower and gnBvnLowerHalf~=0 then depth1=depth1-gnCurrHeight -- Offset the machining half log above the current piece end if use1603 then --000009 1603 04 1340 900 900 900 1340 0 10 0 0 0 000 8 -- len width height angle counterdim protru.0=l,10=r nBvnDir=10-nBvnDir BvnAddLine( nPos, string.format( "%s 1603 %02d %8.0f 900 900 900 %7.0f 0 %2d 0 0 0 000 8 ", sPieceNum, nSide, 10000*BvnGetXc(nPos), 10000*BvnGetXc(dLen), nBvnDir ) ) else BvnAddLine( nPos, string.format( "%s 1601 %02d %8.0f 0 0 %2d %7.0f %7.0f %7.0f %7.0f 0 0 000 ", sPieceNum, nSide, 10000*BvnGetXc(nPos), nBvnDir, 10000*BvnGetXc(dLen), 10000*depth1, 10000*depth2, 10000*depth3) ) end -- Cut bot/top if bToBeg then nPos=BvnGetXc(dLen) else nPos=BvnGetXc(gnCurrTotLen) end if cutTop then -- Cut away top side of the joint depth1 = gnCurrHeight * 0.5 depth1 = gnProfHeight-(depth1-gnBvnYoffset) BvnAddGro(sPieceNum, nPos, 0, nPos, gnCurrWidth, depth1, dLen, 0, 1, 0) end if cutBot then -- Cut away bottom side of the joint depth1 = gnCurrHeight * 0.5 depth1 = depth1-gnBvnYoffset BvnAddGro(sPieceNum, nPos, 0, nPos, gnCurrWidth, depth1, dLen, 0, 3, 0) end end -- Rotates refline to next side function BvnGroRotateToNext(x1, y1, x2, y2, groDepth, groWidth, groAngle, groFace, 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 function BvnAddGroAsBird(sPieceNum, x1, y1, x2, y2, groDepth, groWidth, groAngle, groFace, groAngleLen) local tblGro, i if BvnAddGroAsBirdInt(sPieceNum, x1, y1, x2, y2, groDepth, groWidth, groAngle, groFace, groAngleLen) then return true end -- Try with bottom changed to ref line 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 tblGroInfo = af_request("mc_getgroinfo", tblGro) if tblGroInfo.birdAltGro==nil then return end --AddCncErr( 0, "@@@ALT GRO" ) if BvnAddGroAsBirdInt(sPieceNum, tblGroInfo.birdAltGro.x1, tblGroInfo.birdAltGro.y1, tblGroInfo.birdAltGro.x2, tblGroInfo.birdAltGro.y2, tblGroInfo.birdAltGro.depth, tblGroInfo.birdAltGro.width, tblGroInfo.birdAltGro.groAngleRad, tblGroInfo.birdAltGro.face, tblGroInfo.birdAltGro.groTiltAngleRad) then return true end if false then 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 -- Try all three ways for i=1,3 do af_request("mc_rotgro", tblGro) if BvnAddGroAsBirdInt(sPieceNum, tblGro.x1, tblGro.y1, tblGro.x2, tblGro.y2, tblGro.depth, tblGro.width, tblGro.groAngleRad, groFace, tblGro.groTiltAngleRad) then return true end end end return false end -- Adds groove as birds mouth if possible -- Returns: true=saved, false=not saved function BvnAddGroAsBirdInt(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, nAngle7, nAngle8, nBegEnd, groLen, dx, dy, dz local nBvnSide,bMirrorY -- Must be tilted in both ways to use this machining if math.abs(groAngle)4 then return false end -- Looks like birdsmouth - check if really is --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*180/PI, groAngleLen*180/PI) ) 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 --AddCncErr( x1, "y1>y2" ) -- 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) --AddCncErr(0, 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)) --AddCncErr(0, string.format("tblGroInfo.isBird=%s tblGroInfo.isBirdBeg90=%s tblGroInfo.isBirdEnd90=%s tblGroInfo.birdBegCutFace=%d tblGroInfo.birdEndCutFace=%d", tostring(tblGroInfo.isBird), tostring(tblGroInfo.isBirdBeg90), tostring(tblGroInfo.isBirdEnd90), tblGroInfo.birdBegCutFace, tblGroInfo.birdEndCutFace)) -- Calculate groove bottom (y1PI*89/180 or angleEndPI*89/180 then --AddCncErr(x1, string.format("CANNOT SAVE AS BIRDSMOUTH angleBeg=%f angleEnd=%f", angleBeg*180/PI, angleEnd*180/PI)) return false end -- Anchor to y0 (may be swapped later) if math.abs(dyDir)>0.001 then -- Should be always x=x-y/dyDir*dxDir z=z+y/dyDir*dzDir y=0 end -- Gro angle (=angle in k2.exe) relative to bottom plane dx=-dxBot dz=-dzBot d=math.sqrt(dx*dx+dz*dz) if d<0.0001 then AddCncErr(x1, string.format("INTERNAL ERROR IN BIRDSMOUTH: d<0.0001, SAVED AS NORMAL GROOVE")) return false end dx=dx/d dz=dz/d angleGro=math.acos(dx*dxDir + dz*dzDir) --AddCncErr(0, string.format("x=%f y=%f z=%f angleGro=%f angleBeg=%f angleEnd=%f", x, y, z, angleGro/PI*180, angleBeg/PI*180, angleEnd/PI*180)) nLeft=0 if groAngleLen<0 then nLeft=1-nLeft end nBvnSide,bMirrorY=BvnGetSide(groFace) if gnBvnLogDir==EBvnDirBeg then nBvnSide=-nBvnSide nLeft=1-nLeft end -- Is end not 90 deg? if tblGroInfo.isBirdEnd90==false then if tblGroInfo.isBirdBeg90==false then -- Cannot use birdsmouth to save this return false end nBvnSide=-nBvnSide angleBeg,angleEnd=angleEnd,angleBeg nLeft=1-nLeft end if angleGro>PI2 then angleGro=PI2-(angleGro-PI2) end if angleGroPI*89/180 then --AddCncErr(x1, string.format("CANNOT SAVE AS BIRDSMOUTH angleGro=%f", angleGro*180/PI)) return false end if z<0.0011 then -- Change anchor side to face width local dy dy=gnProfHeight if logFace==1 or logFace==3 then dy=gnCurrWidth end y=dy-y x=x+y/dyDir*dxDir z=z-y/dyDir*dzDir y=dy end x=BvnGetXc(x) if bMirrorY then y=BvnMirrorYc(y, groFace) nLeft=1-nLeft end --0202 -3 35381 1130 158 450 549 0 10 0 000 BvnAddLine( x, string.format( "%s 0202 %02d %8.0f %7.0f %7.0f %7.0f %7.0f 0 %2d 0 000 ", sPieceNum, nBvnSide, 10000*x, 10000*y, angleEnd/PI*1800, angleGro/PI*1800, z*10000, nLeft*10 ) ) -- 3D pocket tested 12/2012: Cannot do any other angle but 90 deg - no use return true end -- Adds groove as vcut at beg/end if possible -- Returns: true=saved, false=not saved function BvnAddGroAsVCut(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, nAngle7, nAngle8, nBegEnd, groLen, dx, dy, dz local nBvnSide,bMirrorY local vSideOrg, bSwapLR 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 bSwapLR=false if groFace>4 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)PI180 or math.abs(groAngleLen)>PI180 then AddCncErr(x1, string.format("WARNING: Groove 150x, skipping angle(s) - writing with zero angle(s)")) end if groFace>4 then AddCncErr(x1, string.format("WARNING: Groove 150x can be only on surfaces 1-4 (set to %d), writing as normal groove", groFace)) return false end is1500=true inside=0 if groType==5 or groType==7 then is1500=false end if groType==6 or groType==7 then inside=10 end -- Calculate coordinates so that x-coordinates are changing and y remains the same (in plank's direction) if math.abs(y1-y2)>0.0005 then -- Swap direction if math.abs(x2-x1)>0.0005 then AddCncErr(x1, string.format("WARNING: Groove 150x can only be in plank's direction (x1=%f y1=%f x2=%f y2=%f), writing as normal groove", x1, y1, x2, y2)) return false end if y1>y2 then -- Oops, y1 smaller x2=x1+groWidth groWidth=y1-y2 y1=y2 else x1=x2-groWidth groWidth=y2-y1 y2=y1 end end -- x to bvn x1=BvnGetXc(x1) x2=BvnGetXc(x2) if x1>x2 then x1,x2=x2,x1 end nBvnSide,bMirrorY=BvnGetSide(groFace) -- x10.001 then if y1+groWidth+0.001gdBvnWarnGroWidth then return end if math.abs(x2-x1)<0.001 then if x1+0.001>dx or x1-groWidth-0.001<0 then return end end if math.abs(y2-y1)<0.001 then if y1+0.001>dy or y1-groWidth-0.001<0 then return end end AddCncErr(x1, string.format("WARNING: Groove width is %s - please check", ac_environment ("ntos", groWidth, "length", "work"))) end -- Returns: true=is at end, false=nope function GroAtEnd(x1,x2,y1,y2,groWidth) if math.abs(x2-x1)x2 then x1,x2=x2,x1 end local res if x1<0.001 or x2>gnCurrTotLen-0.001 then res=true end return res end function BvnDirToAngles(dirUpX, dirUpY, dirUpZ) local angle=math.atan2(dirUpX, dirUpZ) local bevel=-math.asin(dirUpY) return angle, bevel end function BvnAddGroAsDrilling(sPieceNum, x1, y1, x2, y2, groDepth, groWidth, groAngle, groFace, groAngleLen) local 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 tblGroInfo = af_request("mc_getgroinfo", tblGro) local lenBot=tblGroInfo.lenBot local midPtBotX=x1+tblGroInfo.vecDownSurf.x*groDepth + tblGroInfo.vecLeftSurf.x*groWidth/2 + tblGroInfo.vecLenSurf.x*lenBot/2 local midPtBotY=y1+tblGroInfo.vecDownSurf.y*groDepth + tblGroInfo.vecLeftSurf.y*groWidth/2 + tblGroInfo.vecLenSurf.y*lenBot/2 local midPtBotZ= tblGroInfo.vecDownSurf.z*groDepth + tblGroInfo.vecLeftSurf.z*groWidth/2 + tblGroInfo.vecLenSurf.z*lenBot/2 local hasX, x, y, _z, distToPlane=LinePlaneX( 0, 0, 1, 0, midPtBotX, midPtBotY, midPtBotZ, midPtBotX-tblGroInfo.vecDownSurf.x, midPtBotY-tblGroInfo.vecDownSurf.y, midPtBotZ-tblGroInfo.vecDownSurf.z ) if not hasX then AddCncErr(x1, string.format("ERROR: Drilling does intersect plane")) return end local side, mirrorY=BvnGetSide(groFace) local vecUpX=-tblGroInfo.vecDownSurf.x local vecUpY=-tblGroInfo.vecDownSurf.y local vecUpZ=-tblGroInfo.vecDownSurf.z if mirrorY then y=BvnMirrorYc(y, groFace) vecUpY=-vecUpY end if gnBvnLogDir==EBvnDirEnd then vecUpX=-vecUpX end local angle, bevel=BvnDirToAngles( vecUpX, vecUpY, vecUpZ ) local groDiameter=TernaryIf(lenBot7 then if BvnAddGroAsBird(sPieceNum, x1, y1, x2, y2, groDepth, groWidth, groAngle, groFace, groAngleLen) then return end if BvnAddGroAsVCut(sPieceNum, x1, y1, x2, y2, groDepth, groWidth, groAngle, groFace, groAngleLen) then return end end if groType<4 or groType>7 then if BvnAddGroAsBird(sPieceNum, x1, y1, x2, y2, groDepth, groWidth, groAngle, groFace, groAngleLen) then return end if BvnAddGroAsVCut(sPieceNum, x1, y1, x2, y2, groDepth, groWidth, groAngle, groFace, groAngleLen) then return end end if x1<-1 or x1>gnCurrTotLen+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 BVN FILE ANYWAY: x1=%.0f y1=%.0f x2=%.0f y2=%.0f depth=%.0f", x1*1000, y1*1000, x2*1000, y2*1000, groDepth*1000) ) end if groType>=4 and groType<=7 then -- With codes 1500 or 1501 if BvnSaveGroAs15(sPieceNum, x1, y1, x2, y2, groDepth, groWidth, groAngle, groFace, groAngleLen, groType) then return end end local nSplinter, nGroLen, dx, dy if gbHonkaDt==true then if groWidth<0.0599 then AddCncErr( x1, string.format("VAROITUS/VIRHE: HONKA NOLLANURKKA, URAN LEVEYS<60 MM (%.2f), TEHD��N MAHDOLLISESTI NOLLANURKKATY�KALULLA", groWidth*1000) ) end end nSplinter=100 if gbBvnNoSplinterFree then nSplinter=0 end --toLog( string.format( "GRO1, x1=%.3f y1=%.3f x2=%.3f y2=%.3f groDepth==%.3f groWidth=%.3f groAngle=%.3f groFace=%.3f groAngleLen=%.3f", x1, y1, x2, y2, groDepth, groWidth, groAngle, groFace, groAngleLen ) ) if math.abs(y2-y1) EPS x1, y1, x2, y2, groDepth, groWidth, groAngle, groAngleLen = BvnGroRotateToNext(x1, y1, x2, y2, groDepth, groWidth, groAngle, groFace, groAngleLen) end end -- y1y2+EPS then x1, y1, x2, y2, groDepth, groWidth, groAngle, groAngleLen = BvnGroRotateToNext(x1, y1, x2, y2, groDepth, groWidth, groAngle, groFace, groAngleLen) x1, y1, x2, y2, groDepth, groWidth, groAngle, groAngleLen = BvnGroRotateToNext(x1, y1, x2, y2, groDepth, groWidth, groAngle, groFace, groAngleLen) end orgY1=y1 minY=y1 maxY=y2 -- Unit vector for the groove ux=x2-x1 uy=y2-y1 nGroLen=math.sqrt(ux*ux + uy*uy) if nGroLen0.001 and (groFace==2 or groFace==4) then -- Not a horizontal groove, check if to top of the log (=rise) and adjust to profile height if yes (1 mm threshold) if y1+0.001>gnCurrHeight and y1y2 then x1=x1-ux*(gnProfHeight-y1) y1=gnProfHeight end if y2+0.001>gnCurrHeight and y2y1 then x2=x2+ux*(gnProfHeight-y2) y2=gnProfHeight end end if gnBvnYoffset~=0 then -- Groove coordinates are always to whole log but we have half log raw material. if groFace==2 or groFace==4 or groFace==5 or groFace==6 then y1=y1-gnBvnYoffset y2=y2-gnBvnYoffset end if groFace==3 then -- Bottom face --5/2011, values to real profile: groDepth=groDepth-gnBvnYoffset end end if groFace>4 then -- Beg/end local anchorSide, isHor, anchorHeight, anchorWidth local tblGroInfo, tblGro, bMirrorXEnd2 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 tblGroInfo = af_request("mc_getgroinfo", tblGro) -- Alkup�� tai loppup�� -- beg/end anchored to log/plank bottom face -- Beg&End, adjust to midline cosSub=math.cos(groAngle) if cosSub<0.001 then cosSub=0.001 -- Impossible groove anyway end CheckGroWarn(x1,y1,x2,y2,groWidth,gnCurrWidth,gnProfHeight) unx=-uy*groWidth*0.5/cosSub uny=ux*groWidth*0.5/cosSub -- Move to the center line x1=x1+unx y1=y1+uny x2=x2+unx y2=y2+uny groDepth=groDepth-groWidth*0.5*math.tan(groAngle) if groFace==5 then ux=0 else ux=gnCurrTotLen end anchorSide=3 -- In Frame sides anchorHeight=gnProfHeight anchorWidth=gnCurrWidth isHor=false if math.abs(x2-x1)>0.001 then -- Horizontal at begin/end, swap coordinates if math.abs(y2-y1)>0.001 then AddCncErr( 0, "BVN ERROR: GROOVE AT THE END OF A PIECE SKIPPED - ONLY VERTICAL AND HORIZONTAL GROOVES ARE SUPPORTED" ) return end y1,x1,y2,x2=x1,gnProfHeight-y1,x2,gnProfHeight-y2 --groAngleLen=-groAngleLen anchorSide=4 anchorHeight=gnCurrWidth anchorWidth=gnProfHeight isHor=true end if groFace==5 then nBvnSide,bMirrorXEnd,bMirrorY=BvnGetLogBegBvnSide(anchorSide) else nBvnSide,bMirrorXEnd,bMirrorY=BvnGetLogEndBvnSide(anchorSide) end if math.abs(groAngleLen)>PI1800 then -- Lengthwise angled at the end - check if infinite if tblGroInfo.begIn and tblGroInfo.endIn then nGroLen=0 end if math.abs(groAngle)>PI1800 then AddCncErr( x1, "BVN ERROR: ANGLED GROOVE NOT SUPPORTED BEG/END - GROOVE NOT ANGLED ***" ) end -- Adjust params to mid point a = PI2 + groAngleLen y=y1 if isHor then temp=3 if gnBvnLogDir==EBvnDirEnd then temp=1 end else temp=2 if gnBvnLogDir==EBvnDirEnd then temp=4 end end if groFace==5 then nBvnSide,bMirrorXEnd2,bMirrorY=BvnGetLogBegBvnSide(temp) else nBvnSide,bMirrorXEnd2,bMirrorY=BvnGetLogEndBvnSide(temp) end if bMirrorY then a=PI-a else y=anchorHeight - y end x = (x1+x2)*0.5 groDepth=groDepth+(x-x1)*math.sin(groAngleLen) if bMirrorXEnd then x=anchorWidth-x end if bMirrorY then y=anchorHeight-y end --y = (y1+y2)*0.5 ux=BvnGetXc(ux) BvnAddLine( ux, string.format( "%s 0304 %02d %8.0f %7.0f %7.0f %7.0f %7.0f %7.0f %7.0f 0 0 0 %3.0f ", sPieceNum, nBvnSide, 10000*ux, 10000*y, 10000*x, 10000*nGroLen, 10000*groWidth, 10000*groDepth, 1800*a/PI, nSplinter ) ) else -- OLD GROOVE EXPAND HERE if y1<0.001 then -- Expand >tool radius below y1=-0.030 end if y2>gnCurrHeight-0.001 then -- Expand >tool radius above y2=anchorHeight+0.030 end if (y1<0.001 and y2>gnCurrHeight-0.001) or (minY <0.001 and maxY>gnCurrHeight-0.001) then nGroLen=0 -- Through else nGroLen=y2-y1 -- Limited end x = (x1+x2)*0.5 if bMirrorXEnd then x=anchorWidth-x end y = (y1+y2)*0.5 if bMirrorY then y=anchorHeight-y end ux=BvnGetXc(ux) a=PI2+groAngle --[[ Fixed and tested 8/2020 if math.abs(groAngle)>PI1800 then AddCncErr( x1, "BVN WARNING: ANGLED GROOVE BEG/END NOT FULLY CHECKED - CHECK CAREFULLY ***" ) end ]] BvnAddLine( ux, string.format( "%s 0304 %02d %8.0f %7.0f %7.0f %7.0f %7.0f %7.0f %7.0f 0 0 0 %3.0f ", sPieceNum, nBvnSide, 10000*ux, 10000*x, 10000*y, 10000*groWidth, 10000*nGroLen, 10000*groDepth, 1800*a/PI, nSplinter ) ) end return end -- # Groove to side local tblGroInfo, tblGro nBvnSide,bMirrorY=BvnGetSide(groFace) 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 tblGroInfo = af_request("mc_getgroinfo", tblGro) -- Use general 0306? (could actually make all grooves with this) local use0306 use0306=false if math.abs(groAngleLen)>PI1800 and (math.abs(groAngle)>PI1800 or math.abs(x2-x1)>0.001 or y2-y1<0.001) then use0306=true end if math.abs(groAngle)>PI1800 and math.abs(x2-x1)>0.0001 then use0306=true end if groFace==2 or groFace==4 then CheckGroWarn(x1,y1,x2,y2,groWidth,gnCurrTotLen,gnProfHeight) else CheckGroWarn(x1,y1,x2,y2,groWidth,gnCurrTotLen,gnCurrWidth) end if use0306 then -- Groove still in Frame coordinates local aLen, aSurf, tblGro90, lenNow, len90, info90 -- Gives just length of y-component local fnGetLenOnSurf = function(groFace, y1, y2) if y1>y2 then y2, y1 = y1, y2 end if y1<0 then y1=0 end if y1>faceHeight then y1=faceHeight end if y2<0 then y2=0 end if y2>faceHeight then y2=faceHeight end return y2-y1 end -- 0306 always makes infinite cut - check if rotated 90 gives longer intersection with the target surface (just checking y-coords) -- Since it is infinite cut, the ends may not be inside the plank to use 0306 local nowGood, rotGood tblGro90=deepcopy(tblGro) af_request("mc_rotgro", tblGro90) info90 = af_request("mc_getgroinfo", tblGro90) nowGood=(tblGroInfo.begIn==0 and tblGroInfo.endIn==0) rotGood=(info90.begIn==0 and info90.endIn==0) --ac_environment("tolog", string.format("PRE: tblGroInfo.begIn=%d, tblGroInfo.endIn=%d, nowGood=%s, info90.begIn=%d, info90.endIn=%d, rotGood=%s", tblGroInfo.begIn, tblGroInfo.endIn, tostring(nowGood), info90.begIn, info90.endIn, tostring(rotGood))) --ac_environment("tolog", 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)) lenNow=fnGetLenOnSurf(groFace, y1, y2) len90=fnGetLenOnSurf(groFace, tblGro90.y1, tblGro90.y2) if rotGood and (len90>lenNow or nowGood==false) then -- We have rotated in tblGro90 already but this is nicer way to set all gro pars x1, y1, x2, y2, groDepth, groWidth, groAngle, groAngleLen = BvnGroRotateToNext(x1, y1, x2, y2, groDepth, groWidth, groAngle, groFace, groAngleLen) tblGroInfo = af_request("mc_getgroinfo", tblGro90) nowGood=rotGood end if not nowGood then -- This is a limited groove, cannot use 0306, use 0308 (3D pocket) instead -- tblGro is unrotated and tblGro90 is the rotated one, select the one not having lengthwise angle if math.abs(tblGro.groTiltAngleRad)>PI1800 then tblGro=tblGro90 tblGroInfo=info90 if math.abs(tblGro.groTiltAngleRad)>PI1800 then AddCncErr( x1, "BVN ERROR: DOUBLE ANGLED GROOVE WRITTEN WITHOUT LENGTHWISE ANGLE" ) tblGro.groTiltAngleRad=0 end end --DumpTbl(tblGroInfo) -- # Use tblGro values since we may be using rotated one!!! -- 0308 takes center point of the bottom projected to the surface -- Calculate it x=tblGro.x1 + tblGroInfo.vecDownSurf.x * tblGro.depth y=tblGro.y1 + tblGroInfo.vecDownSurf.y * tblGro.depth z=tblGroInfo.vecDownSurf.z * tblGro.depth -- Negative inside the plank x=x+tblGroInfo.vecLeftSurf.x * tblGro.width*0.5 y=y+tblGroInfo.vecLeftSurf.y * tblGro.width*0.5 z=z+tblGroInfo.vecLeftSurf.z * tblGro.width*0.5 -- Lengthwise angle is zero, we can use length of the groove as it is dx=tblGro.x2 - tblGro.x1 dy=tblGro.y2 - tblGro.y1 nGroLen=math.sqrt(dx*dx+dy*dy) x=x+tblGroInfo.vecLenSurf.x * nGroLen*0.5 y=y+tblGroInfo.vecLenSurf.y * nGroLen*0.5 z=z+tblGroInfo.vecLenSurf.z * nGroLen*0.5 a =tblGro.groAngleRad*180/PI ux =tblGroInfo.vecLenSurf.x uy =tblGroInfo.vecLenSurf.y x=BvnGetXc(x) if gnBvnLogDir==EBvnDirEnd then ux=-ux end if bMirrorY then y=BvnMirrorYc(y, groFace) uy=-uy end --AddCncErr( 0, string.format("bMirrorY=%s nBvnSide=%d", tostring(bMirrorY), nBvnSide) ) aSurf=math.atan2(ux, uy) if groFace==3 or groFace==4 then --a=-a aSurf=-math.atan2(ux, -uy) end aSurf=aSurf*180.0/PI if aSurf<0 then aSurf=360+aSurf end --ac_environment("tolog", string.format("x=%f y=%f z=%f aSurf=%f a=%f", x, y, z, aSurf, a)) -- 000006 0308 01 5000 1820 300 4000 500 0 890 30 110 0 000 BvnAddLine( x1, string.format( "%s 0308 %02d %8.0f %7.0f %7.0f %7.0f %7.0f 0 %7.0f 0 %7.0f 0 000 ", sPieceNum, nBvnSide, 10000*x, 10000*y, -10000*z, 10000*nGroLen, 10000*tblGro.width, 10*aSurf, 10*a ) ) return end -- 0306 takes gro bottom edge coordinate projected to surface, calc x,y,z at gro beg x=x1+tblGroInfo.vecDownSurf.x*groDepth y=y1+tblGroInfo.vecDownSurf.y*groDepth z=-tblGroInfo.vecDownSurf.z*groDepth a =-groAngle*180/PI --aLen =groAngleLen*180/PI aLen =math.atan2(-tblGroInfo.vecLenSurf.z, math.sqrt(tblGroInfo.vecLenSurf.x*tblGroInfo.vecLenSurf.x + tblGroInfo.vecLenSurf.y*tblGroInfo.vecLenSurf.y)) a=a/math.abs(math.cos(aLen)) -- Cannot really understand this, but this gives right angle aLen =aLen*180/PI ux =tblGroInfo.vecLenSurf.x uy =tblGroInfo.vecLenSurf.y x=BvnGetXc(x) if gnBvnLogDir==EBvnDirEnd then ux=-ux end if bMirrorY then y=BvnMirrorYc(y, groFace) uy=-uy end --AddCncErr( 0, string.format("bMirrorY=%s nBvnSide=%d", tostring(bMirrorY), nBvnSide) ) aSurf=math.atan2(uy, -ux) if groFace==3 or groFace==4 then --a=-a aLen=-aLen aSurf=-math.atan2(uy, ux) end aSurf=aSurf*180.0/PI --toLog(string.format("groFace=%f aLen=%f aSurf=%f a=%f x=%f y=%f z=%f", groFace, aLen, aSurf, a, x, y, z)) if aSurf<-90 then aSurf=360+aSurf elseif aSurf>270 and aSurf<360.1 then aSurf=-(360-aSurf) end BvnAddLine( x, string.format( "%s 0306 %02d %8.0f %7.0f 0 %7.0f 0 %7.0f %7.0f %7.0f %7.0f 0 %3.0f ", sPieceNum, nBvnSide, 10000*x, 10000*y, 10000*groWidth, 10000*z, aSurf*10, a*10, aLen*10, nSplinter ) ) return end -- Make y1y2 and not bMirrorY) or (y1gnCurrHeight-0.001) or (minY <0.001 and maxY>gnCurrHeight-0.001) then nGroLen=0 end elseif groFace==1 or groFace==3 then if (y1<0.001 and y2>gnCurrWidth-0.001) or (minY <0.001 and maxY>gnCurrWidth-0.001) then nGroLen=0 end end --toLog( string.format("y1=%f y2=%f nGroLen=%f", y1, y2, nGroLen) ) if bMirrorY == BvnIsMirrorXc() then -- Change the side of the groove line. In bvn we anchor the right side of the groove cosSub=math.cos(groAngle) if cosSub<0.001 then cosSub=0.001 -- Impossible groove anyway end unx=-uy*groWidth/cosSub uny=ux*groWidth/cosSub -- Move the ref line x1=x1+unx y1=y1+uny x2=x2+unx y2=y2+uny -- Change depth (neg makes deeper, pos shallower) groDepth=groDepth-math.sin(groAngle)*groWidth/cosSub groAngle=-groAngle end if math.abs(groAngleLen)>PI1800 then if math.abs(groAngle)>PI1800 or math.abs(x2-x1)>0.001 or y2-y1<0.001 then -- After 0306 never reached AddCncErr( x1, "BVN ERROR: LENGTHWISE TILTED GROOVE NOT SUPPORTED WITH GIVEN PARAMETERS - GROOVE SKIPPED" ) return else -- Straight cut angled lengthwise -- Use cutter -- "000001 0302 01 2989 0 0 3000 387 -387 -300.1 0 0 0 000 " local d1, d2 len=gnCurrWidth if groFace==2 or groFace==4 then len=gnProfHeight end -- Depth if y1 is zero d1=groDepth/math.cos(groAngleLen) -- Adjust according to y1 nTan=math.tan(groAngleLen) d1=d1-nTan*y1 --orgY1 d2=d1+nTan*len --toLog(string.format("groAngleLen=%f d1=%f d2=%f len=%f nTan=%f", groAngleLen, d1, d2, len, nTan)) -- k2.exe feature/bug: zero depth means something else if math.abs(d1)<0.0001 then d1=0.0001 end if math.abs(d2)<0.0001 then d2=0.0001 end if false and bMirrorY then temp=d1 d1=d2 d2=temp groAngleLen=-groAngleLen end BvnAddLine( x1, string.format( "%s 0302 %02d %8.0f 0 0 %7.0f %7.0f %7.0f %7.0f 0 0 0 %3.0f ", sPieceNum, nBvnSide, 10000*x1, 10000*groWidth, 10000*d1, 10000*d2, 10*groAngleLen, nSplinter) ) return end end --ac_environment("tolog", string.format("tblGroInfo.begIn=%d, tblGroInfo.endIn=%d", tblGroInfo.begIn, tblGroInfo.endIn)) local groMaxDepth=0.159 local mcEnv mcEnv=af_request("mc_getenv") if mcEnv and mcEnv.gromaxdepth then groMaxDepth=mcEnv.gromaxdepth+EPS end --toLog( string.format( "GRO1, x1=%.3f y1=%.3f x2=%.3f y2=%.3f groDepth==%.3f groWidth=%.3f groAngle=%.3f groFace=%.3f groAngleLen=%.3f nGroLen=%.3f", x1, y1, x2, y2, groDepth, groWidth, groAngle, groFace, groAngleLen, nGroLen ) ) if (nGroLen>0 and nGroLen<0.0196) and math.abs(groAngleLen)faceDepth then d=0 end BvnAddLine( x, string.format( "%s 0502 %02d %8.0f %7.0f 0 %7.0f %7.0f %7.0f 10 0 0 000 ", sPieceNum, nBvnSide, 10000*x, 10000*(y1 + y2)*0.5, 10000*groWidth, 10000*d, 10000*(y2-y1) ) ) elseif groDepth>groMaxDepth and math.abs(groAngleLen)4 then chainFace=1 end yBottom=groDepth yBottom=yBottom-chainThick*0.5 if groFace==1 then yBottom=gnProfHeight-yBottom elseif groFace==4 then yBottom=gnCurrWidth-yBottom end chainFaceBvn,bMirrorY=BvnGetSide(chainFace) if bMirrorY then yBottom=BvnMirrorYc(yBottom, chainFace) end -- 000001 0800 04 4220 1040 0 1640 0 80 0 0 0 0 000 BvnAddLine( x1, string.format( "%s 0800 %02d %8.0f %7.0f 0 %7.0f 0 %7.0f 0 0 0 0 000 ", sPieceNum, chainFaceBvn, 10000*(x1+0.0005), 10000*yBottom, 10000*(groWidth-0.001), 10000*chainThick) ) elseif (tblGroInfo.begIn==1 or tblGroInfo.endIn==1) and (math.abs(groAngleLen)EPS) then -- 5/2016: Groove where x1<>x2, 0301 made top and bottom always in plank's dir -- 000333 0305 01 8000 1550 0 400 1000 200 2000 0 10 900 000 -- 8000 pos -- 1550 y-coordinate -- 400 width of the groove -- 1000 length of the groove -- 200 depth -- 2000 angle -- 10 with corner drill -- 900 inner angle -- 000 splinter free a=1800*math.atan2(y1-y2, x1-x2)/PI BvnAddLine( x1, string.format( "%s 0305 %02d %8.0f %7.0f 0 %7.0f %7.0f %7.0f %7.0f 0 10 900 %3.0f ", sPieceNum, nBvnSide, 10000*x2, 10000*y2, 10000*groWidth, 10000*nGroLen, 10000*groDepth, a, nSplinter ) ) else local d, a2 if math.abs(groAngleLen)>PI1800 then -- After 0306 never reached AddCncErr( x1, "BVN ERROR: LENGTHWISE TILTED GROOVE NOT SUPPORTED WITH GIVEN PARAMETERS - GROOVE SKIPPED" ) return end groAngle=-groAngle -- 0301 ottaa koordinaatit uran pohjaan, syvyys kappaleen pinnasta x=(x1+x2)*0.5 y=(y1+y2)*0.5 a=math.atan2(y2-y1, x1-x2) -- Move center pt (normal to left) dx=-(y2-y1) dy=x2-x1 len=math.sqrt(dx*dx+dy*dy) d=groDepth*math.sin(groAngle) x=x+dx/len*d y=y+dy/len*d groDepth=groDepth*math.cos(groAngle) --toLog( string.format( "GRO2, x1=%.3f y1=%.3f x2=%.3f y2=%.3f groDepth==%.3f groWidth=%.3f groAngle=%.3f groFace=%.3f groAngleLen=%.3f x=%.3f y=%.3f", x1, y1, x2, y2, groDepth, groWidth, groAngle, groFace, groAngleLen, x, y ) ) a=1800*a/PI if math.abs(a)<1 then a=0 end a2=1800*groAngle/PI if math.abs(a2)<1 then a2=0 end BvnAddLine( x1, string.format( "%s 0301 %02d %8.0f %7.0f 0 %7.0f %7.0f %7.0f %7.0f 0 %7.0f 0 %3.0f ", sPieceNum, nBvnSide, 10000*x, 10000*y, 10000*groWidth, 10000*nGroLen, 10000*groDepth, a, a2, nSplinter ) ) end end -- Calculates y-offset here! function BvnAddDrill(sPieceNum, x1, y1, diameter, depth, face) local nBvnSide, bMirrorX, bMirrorY, pos if face~=1 and face~=3 then y1=y1-gnBvnYoffset end if face==5 or face==6 then -- To beg -- 000001 0401 03 0 1025 1025 400 1600 900 0 0 0 0 000 if face==5 then nBvnSide,bMirrorX,bMirrorY=BvnGetLogBegBvnSide(3) pos=BvnGetXc(0) else nBvnSide,bMirrorX,bMirrorY=BvnGetLogEndBvnSide(3) pos=BvnGetXc(gnCurrTotLen) end if bMirrorX then x1=gnCurrWidth-x1 end if bMirrorY then y1=gnProfHeight-y1 end BvnAddLine( pos, string.format( "%s 0401 %02d %8.0f %7.0f %7.0f %7.0f %7.0f 900 0 0 0 0 000 ", sPieceNum, nBvnSide, 10000*pos, 10000*x1, 10000*y1, 10000*diameter, 10000*depth) ) if diameter<0.040-EPS then AddCncErr( 0, "BVN WARNING: DRILLING TO THE BEG OR END MINIMUM DRILL DIAMETER IS 40 MM - SAVED TO BVN ANYWAY" ) end return end nBvnSide,bMirrorY=BvnGetSide(face) x1=BvnGetXc(x1) if bMirrorY then y1=BvnMirrorYc(y1, face) end BvnAddLine( x1, string.format( "%s 0400 %02d %8.0f %7.0f %7.0f %7.0f 000 ", sPieceNum, nBvnSide, 10000*x1, 10000*y1, 10000*diameter, 10000*depth) ) end -- Adds marking to bvn-file function BvnAddMarking(sPieceNum, groFace, x1, y1, x2, y2, xPosText, yPosText, fontSize, textOff, strText, bAddLine, bInLineDir) -- 0600 Line without text, angle possible if inkjet -- 0601 Text with straight line --000001 0600 -3 4000 200 0 0 300 0 0 0 0 0 000 --000001 0601 -3 4000 0 10 0 0 10 TXT 050 local nBvnSide, bMirrorY, pos, res, x, y, angle, orgy1, orgy2 if groFace>=5 then AddCncErr(0, string.format("BVN: MARKINGS NOT SUPPORTED TO THE BEGIN/END OF THE PIECE - SKIPPED") ) return end if nTextHeight==nil then nTextHeight=0 end orgy1=y1 orgy2=y2 nBvnSide,bMirrorY=BvnGetSide(groFace) nBvnSide=-nBvnSide x1=BvnGetXc(x1) x2=BvnGetXc(x2) if bMirrorY then y1=BvnMirrorYc(y1, groFace) y2=BvnMirrorYc(y2, groFace) end if y1>y2 then -- swap x1,x2=x2,x1 y1,y2=y2,y1 end if bAddLine==nil then bAddLine=true end -- Extend to line so that y is 0 -- 1. bool, true=has intersection -- 2. number, x -- 3. number, y x=x1 y=y1 res,x1,y1=GetLinesX( x1, y1, x2, y2, 0, 0, 1, 0 ) if res==false and bAddLine then AddCncErr( x1, string.format("BVN WARNING: MARKER DOES NOT CROSS THE PLANK - SKIPPED (X1=%.3f Y1=%.3f X2=%.3f Y2=%.3f)", x, y, x2, y2) ) return end if res==false then x1=x y1=y end angle=math.atan2(x2-x1, y2-y1) if math.abs(angle)0.001 and bInLineDir then -- 90deg to the plank xPosText,yPosText=yPosText,xPosText -- Swap - alignment if orgy10.5 then AddCncErr( x, string.format("ERROR: ROTATED DOVETAIL NOT SUPPORTED IN HUNDEGGER - SKIPPED (rot angle=%.3f) ***", rotHun) ) else local borderBot borderBot=tblTenonRes.hunBackCut if borderBot<0 then tenonHeight=tenonHeight+borderBot borderBot=0 end BvnAddLine( x, string.format( "%s 1700 %02d %8.0f %7.0f 0 %7.0f %7.0f %7.0f %7.0f %7.0f %7.0f %7.0f 000 ", sPieceNum, nBvnSide, 10000*x, 10000*y, 10*bevelHun, 10000*tenonWidth, 10000*borderBot, 10*nAngleTenonDeg, 10*(angleHun-90), 10000*tenonHeight, 10000*tenonLen ) ) end elseif nType==3 then -- Mortise if math.abs(rotHun)>0.5 then AddCncErr( x, string.format("ERROR: ROTATED MORTISE AT END NOT SUPPORTED IN HUNDEGGER - SKIPPED ***") ) else --Known limitation: len must be > width. No workaround if tenonWidth>tenonHeight+0.0001 then AddCncErr( x, string.format("ERROR: MORTISE WIDTH > HEIGHT, NOT SUPPORTED IN HUNDEGGER (SAVED INTO FILE ANYWAY)") ) end tenonLen=tenonLen+gMortHousing if math.abs(nAngleTopDeg-90)>0.5 then if math.abs(nAngleSideDeg-90)>0.5 then AddCncErr( x, string.format("ERROR: DOUBLE TILTED MORTISE END NOT SUPPORTED IN HUNDEGGER - SKIPPED ***") ) return end -- Top angle only BvnAddLine( x, string.format( "%s 0505 %02d %8.0f %7.0f 0 %7.0f %7.0f %7.0f %7.0f %7s %7.0f 0 000 ", sPieceNum, nBvnSide, 10000*x, 10000*y, 10*nAngleTopDeg, 10000*tenonWidth, 10000*tenonLen, 10000*tblTenonRes.hunBackCut, sRoundType, 10000*tenonHeight ) ) else -- Bevel only or straight. BvnAddLine( x, string.format( "%s 0504 %02d %8.0f %7.0f 0 %7.0f %7.0f %7.0f %7.0f %7s %7.0f 0 000 ", sPieceNum, nBvnSide, 10000*x, 10000*y, 10*nAngleSideDeg, 10000*tenonWidth, 10000*tenonLen, 10000*tblTenonRes.hunBackCut, sRoundType, 10000*tenonHeight ) ) end end elseif nType==4 then -- DT Mortise if math.abs(rotHun)>0.5 then AddCncErr( x, string.format("ERROR: ROTATED DT-MORTISE AT END NOT SUPPORTED IN HUNDEGGER - SKIPPED ***") ) else --Known limitation: len must be > width. No workaround --if tenonWidth>tenonHeight+0.0001 then -- AddCncErr( x, string.format("ERROR: MORTISE WIDTH > HEIGHT, NOT SUPPORTED IN HUNDEGGER (SAVED INTO FILE ANYWAY)") ) --end if gMortHousing~=0 then AddCncErr( x, string.format("ERROR: HOUSING VALUE NONZERO AT THE END OF THE PIECE FOR DT-MORTISE - HOUSING SKIPPED ***") ) end if math.abs(nAngleTopDeg-90)>0.5 then if math.abs(nAngleSideDeg-90)>0.5 then AddCncErr( x, string.format("ERROR: DOUBLE TILTED DT-MORTISE END NOT SUPPORTED IN HUNDEGGER - SKIPPED ***") ) return end -- Top angle only BvnAddLine( x, string.format( "%s 1705 %02d %8.0f %7.0f 0 %7.0f %7.0f %7.0f %7.0f 0 0 %7.0f 000 ", sPieceNum, nBvnSide, 10000*x, 10000*y, 10*nAngleTopDeg, 10000*tenonWidth, 10000*tblTenonRes.hunBackCut, 10*nAngleTenonDeg, 10000*tenonLen ) ) else -- Bevel only or straight. BvnAddLine( x, string.format( "%s 1704 %02d %8.0f %7.0f 0 %7.0f %7.0f %7.0f %7.0f 0 0 %7.0f 000 ", sPieceNum, nBvnSide, 10000*x, 10000*y, 10*nAngleSideDeg, 10000*tenonWidth, 10000*tblTenonRes.hunBackCut, 10*nAngleTenonDeg, 10000*tenonLen ) ) end end end end -- nType ETypeXXX: 1=tenon, 2=dt, 3=mortise, 4=dt-pocket, 5=Honka dove, 6=Honka mortise function BvnAddBegEndTenonMortNew( sPieceNum, isBeg, nAngleTopDeg, nAngleSideDeg, x, y, z, nSide, nType, nRotAngleDeg, tenonLen, tenonWidth, tenonHeight, roundingRadius, nAngleTenonDeg ) if nType==5 then -- Honka DT corner tenon local sType, toR, x nType=3 -- Made with mortise tool sType = ac_objectget( "hirsityyppi") if sType==nil then sType="?" end -- Convert to top if nSide~=3 then AddCncErr( 0, string.format("VIRHE NOLLANURKKA: ALKUPER�INEN TAPPI PIT�� OLLA ANKKUROITU ALAPINTAAN (3), TY�ST� OHITETTU") ) return end nSide=1 nAngleTopDeg=180-nAngleTopDeg if math.abs(nAngleTopDeg-45)<1 then toR=true elseif math.abs(nAngleTopDeg-135)<1 then toR=false else AddCncErr( 0, string.format("VIRHE NOLLANURKKA: NURKKA EI OLE 90 ASTEEN NURKKA (%f)", nAngleTopDeg) ) return end -- Calculate offsets to add to x-coordinate (dir=from end inside the plank) and to y-coordinate (towards middle of the plank) local gapx, gapy gapx=0 gapy=0 if gnZeroTailGap~=nil then -- Honka Logs cnc-writer, now only 90 deg corners gapx=gnZeroTailGap/1.4142 gapy=gapx end if math.abs(gnCurrWidth-0.204)89.0 then -- local area=TernaryIf(bevelDegL>89.0, 10, 20) -- BvnAddLine(x, BvnFormatHipRidgeCut(sPieceNum, nSide, x, faceWidth, faceHeight-y, area)) -- else -- BvnAddLine(x, BvnFormatDaboSawCut(sPieceNum, nSide, x, y, faceWidth, bevelDegL, sawLocationLeft)) -- if bevelDegR<-89.0 or bevelDegR>89.0 then -- local area=TernaryIf(bevelDegR>89.0, 10, 20) -- BvnAddLine(x, BvnFormatHipRidgeCut(sPieceNum, nSide, x, faceWidth, faceHeight-y, area)) -- else -- BvnAddLine(x, BvnFormatDaboSawCut(sPieceNum, nSide, x, y, faceWidth, bevelDegR, sawLocationRight)) -- end BvnAddLine(x, BvnFormatDaboSawCut(sPieceNum, nSide, x, y, faceWidth, bevelDegL, sawLocationLeft)) BvnAddLine(x, BvnFormatDaboSawCut(sPieceNum, nSide, x, y, faceWidth, bevelDegR, sawLocationRight)) end function BvnAddBegEndVCut( sPieceNum, isBeg, nSide, x, y, angleDegR, bevelDegR, angleDegL, bevelDegL ) local nBvnSide, nPos, bMirrorY, height, b if isBeg then nPos=x else nPos=gnCurrTotLen-x end nSide=1+((nSide +1)%4) -- To the other side nBvnSide,bMirrorY=BvnGetSide(nSide) if isBeg then nBvnSide = BvnGetLogBegBvnSide(nSide) else nBvnSide = BvnGetLogEndBvnSide(nSide) end if nSide==1 or nSide==3 then y=y+gnCurrWidth*0.5 else y=y+gnProfHeight*0.5 end --angleDegR=180-angleDegR --angleDegL=180-angleDegL b=bMirrorY if not isBeg then b=not b end if nSide==3 or nSide==4 then b=not b end if b then y=BvnMirrorYc(y, nSide) else angleDegR, angleDegL = angleDegL, angleDegR bevelDegR, bevelDegL = bevelDegL, bevelDegR end height=0 --000001 0103 03 880 1000 0 1700 1118 450 1118 900 0 0 000 nPos = BvnGetXc(nPos) BvnAddLine( nPos, string.format( "%s 0103 %02d %8.0f %7.0f 0 %7.0f %7.0f %7.0f %7.0f %7.0f 0 0 000 ", sPieceNum, nBvnSide, 10000*nPos, 10000*y, 10000*height, 10*angleDegR, 10*bevelDegR, 10*angleDegL, 10*bevelDegL ) ) end function BvnAddHiddenShoe( isBeg, sPieceNum, holeWidth, holeHeight, holeDepth, groDepth, groWidth, drillLowest, drillFromEnd, drillSize, drillSpacing, drillCount, side14, drillFromEnd2 ) --Esim: --000931 0304 04 0 0 670 3050 620 50 900 10 000 --000931 0400 02 860 1325 130 0 000 --000931 0400 02 860 925 130 0 000 --000931 0400 02 860 525 130 0 000 --000931 0800 01 30 670 1070 1575 80 000 -- local xc, yc, nWidth, bMirrorY, nBvnSide, xDrill, xDrill2, nDrill, xGro, nextSide, sideHeight, drillHeight local hasDrill2 if side14==nil then -- Anchor added 12/2013 side14=1 end if side14<1 or side14>4 then side14=1 end if drillFromEnd2==nil then drillFromEnd2=0 end hasDrill2=false if drillFromEnd2>0.0001 then hasDrill2=true end xc=0 xDrill=drillFromEnd xDrill2=drillFromEnd2 xGro=0.003 sideHeight=gnCurrWidth drillHeight=gnProfHeight if side14==2 or side14==4 then sideHeight=gnProfHeight drillHeight=gnCurrWidth end if not isBeg then -- At the left side/end in bvn-coordinates xc=gnCurrTotLen xDrill=gnCurrTotLen-drillFromEnd xDrill2=gnCurrTotLen-drillFromEnd2 xGro=gnCurrTotLen-xGro end xc=BvnGetXc(xc) xDrill=BvnGetXc(xDrill) xDrill2=BvnGetXc(xDrill2) xGro=BvnGetXc(xGro) -- P��tyloveus --000931 0304 04 0 0 670 3050 620 50 900 10 000 nextSide=side14+1 if nextSide>4 then nextSide=nextSide-4 end if gnBvnLogDir==EBvnDirBeg then if isBeg then nBvnSide=BvnGetLogBegBvnSide(nextSide) else nBvnSide=BvnGetLogEndBvnSide(nextSide) end else nBvnSide=side14+3 if nBvnSide>4 then nBvnSide=nBvnSide-4 end if isBeg then nBvnSide=BvnGetLogBegBvnSide(nBvnSide) else nBvnSide=BvnGetLogEndBvnSide(nBvnSide) end end BvnAddLine( xc, string.format( "%s 0304 %02d %8.0f 0 %7.0f %7.0f %7.0f %7.0f 900 10 000 ", sPieceNum, nBvnSide, 10000*xc, 10000*sideHeight*0.5, 10000*holeHeight*2, 10000*holeWidth, 10000*holeDepth ) ) -- Poraukset nBvnSide,bMirrorY=BvnGetSide(nextSide) yc=drillLowest if nextSide==3 or nextSide==4 then bMirrorY=not bMirrorY end --toLog(string.format("nBvnSide=%d,bMirrorY=%s", nBvnSide, tostring(bMirrorY))) if not bMirrorY then yc=drillHeight-yc drillSpacing=-drillSpacing end for nDrill=1,drillCount do BvnAddLine( xDrill, string.format( "%s 0400 %02d %8.0f %7.0f %7.0f 0 000 ", sPieceNum, nBvnSide, 10000*xDrill, 10000*yc, 10000*drillSize ) ) if hasDrill2 then BvnAddLine( xDrill2, string.format( "%s 0400 %02d %8.0f %7.0f %7.0f 0 000 ", sPieceNum, nBvnSide, 10000*xDrill2, 10000*yc, 10000*drillSize ) ) end yc=yc+drillSpacing end -- Ura -- 000931 0800 01 30 670 1070 1575 80 000 if isBeg then nBvnSide=BvnGetLogBegBvnSide(side14) else nBvnSide=BvnGetLogEndBvnSide(side14) end BvnAddLine( xGro, string.format( "%s 0800 %02d %8.0f %7.0f %7.0f %7.0f %7.0f 000 ", sPieceNum, nBvnSide, 10000*xGro, 10000*sideHeight*0.5, 10000*groDepth, 10000*holeHeight, 10000*groWidth ) ) end -- Kun palkki menee kapuloiden l�pi function BvnAddBalkMid(sPieceNum, midPos, width, depthTop, depthBot, depthSides) --000007 0300 01 35440 1850 0 1120 0 100 100 0 0 0 000 --000007 0300 03 35440 1850 0 1120 0 100 100 0 0 0 000 --000007 0300 02 35440 0 0 1120 0 50 50 0 0 0 000 --000007 0300 04 35440 0 0 1120 0 200 200 0 0 0 000 local nSide, nBvnSide midPos=BvnGetXc(midPos) midPos=midPos-width*0.5 -- Yl� if depthTop>0.0001 then nBvnSide=BvnGetSide(1) BvnAddLine( midPos, string.format( "%s 0300 %02d %8.0f 0 0 %7.0f 0 %7.0f %7.0f 0 0 0 000 ", sPieceNum, nBvnSide, 10000*midPos, 10000*width, 10000*depthTop, 10000*depthTop ) ) end -- Etu if depthSides>0.0001 then nBvnSide=BvnGetSide(2) BvnAddLine( midPos, string.format( "%s 0300 %02d %8.0f 0 0 %7.0f 0 %7.0f %7.0f 0 0 0 000 ", sPieceNum, nBvnSide, 10000*midPos, 10000*width, 10000*depthSides, 10000*depthSides ) ) end -- Ala if depthBot>0.0001 then nBvnSide=BvnGetSide(3) BvnAddLine( midPos, string.format( "%s 0300 %02d %8.0f 0 0 %7.0f 0 %7.0f %7.0f 0 0 0 000 ", sPieceNum, nBvnSide, 10000*midPos, 10000*width, 10000*depthBot, 10000*depthBot ) ) end -- Taka if depthSides>0.0001 then nBvnSide=BvnGetSide(4) BvnAddLine( midPos, string.format( "%s 0300 %02d %8.0f 0 0 %7.0f 0 %7.0f %7.0f 0 0 0 000 ", sPieceNum, nBvnSide, 10000*midPos, 10000*width, 10000*depthSides, 10000*depthSides ) ) end end -- rotAngleDeg may be nil -- sRoundType nil=default, value=mortise shape given function BvnAddSideJoint( sPieceNum, logFace, bDove, midPos, borderBot, tenonLen, tenonWidth, tenonHeight, angleDove, roundingRadius, rotAngleDeg, housingDepth, sRoundType ) local nBvnSide, bMirrorY, xc, yc, y1, y2, s, minDist, ux, faceHeight, rotAngleDegOrg, a if rotAngleDeg==nil then rotAngleDeg=0 end if housingDepth==nil then housingDepth=0 end if logFace>4 then -- beg/end nBvnSide=9 bMirrorY=false xc=midPos else -- Sivuun nBvnSide,bMirrorY=BvnGetSide(logFace) xc=BvnGetXc(midPos) end rotAngleDegOrg=rotAngleDeg if bMirrorY then rotAngleDeg=-rotAngleDeg end if gnBvnLogDir==EBvnDirEnd then rotAngleDeg=-rotAngleDeg end if math.abs(rotAngleDeg)<0.1 then rotAngleDeg=0 end --AddCncErr( 0, string.format("mirrory=%s", tostring(bMirrorY)) ) faceHeight=gnProfHeight if logFace==1 or logFace==3 then faceHeight=gnCurrWidth end local tenonHeightOrg=tenonHeight tenonHeight=math.abs(tenonHeight) -- 2/2022: Negative to limit DT-pocket if bDove then -- Lohenpyrst�tasku -- "000922 1703 03 16521 0 0 10 600 300 60 0 0 380 000 " -- Eikun aina k��nnet��n s=" 0" if bMirrorY then s="10" end if logFace>4 then -- After 1/2013 this is saved as EMcFrAngledBegTenonMort if math.abs(rotAngleDeg)>0.5 then AddCncErr( x, string.format("ERROR: ROTATED DOVETAIL TO BEG/END NOT SUPPORTED IN HUNDEGGER - SKIPPED (rot angle=%.3f) ***", rotAngleDeg) ) return end -- beg/end if gnBvnLogDir==EBvnDirBeg then xc=gnCurrWidth-xc end --toLog( string.format("x1=%f y1=%f x2=%f y2=%f", x1, y1, x2, y2) ) if logFace==5 then nBvnSide=BvnGetLogBegBvnSide(3) ux=BvnGetXc(0) else nBvnSide=BvnGetLogEndBvnSide(3) ux=BvnGetXc(gnCurrTotLen) end -- Pre 1/2013 saved bottom gap to "Add depth" incorrectly --BvnAddLine( xc, string.format( "%s 1704 %02d %8.0f %7.0f 0 900 %7.0f %7.0f %7.0f %7.0f 0 %7.0f 000 ", -- sPieceNum, nBvnSide, 10000*ux, 10000*xc, 10000*tenonWidth, 10000*borderBot, 10*angleDove, 10000*bottomGap, 10000*tenonLen ) ) BvnAddLine( xc, string.format( "%s 1704 %02d %8.0f %7.0f 0 900 %7.0f %7.0f %7.0f 0 0 %7.0f 000 ", sPieceNum, nBvnSide, 10000*ux, 10000*xc, 10000*tenonWidth, 10000*borderBot, 10*angleDove, 10000*tenonLen ) ) else -- Pre 1/2013 put bottom gap to "Add depth" incorrectly --BvnAddLine( xc, string.format( "%s 1703 %02d %8.0f 0 0 %s %7.0f %7.0f %7.0f %7.0f 0 %7.0f 000 ", -- sPieceNum, nBvnSide, 10000*xc, s, 10000*tenonWidth, 10000*borderBot, 10*angleDove, 10000*bottomGap, 10000*tenonLen ) ) -- 2/2022: Allow limiting DT pocket (before was always positive) if tenonHeightOrg>=0 then tenonHeight=0 -- Unlimited end if math.abs(rotAngleDeg)>180.01 then AddCncErr( x, string.format("ERROR: ROTATED DOVETAIL POCKET ANGLE OUT OF LIMITS (rot angle=%.3f), SKIPPED ***", rotAngleDeg) ) else local swapped swapped=false if rotAngleDeg<-90 then swapped=true rotAngleDeg=180+rotAngleDeg end if rotAngleDeg>90 then swapped=true rotAngleDeg=-(180-rotAngleDeg) end if swapped then if s=="10" then s=" 0" else s="10" end borderBot=faceHeight-borderBot end if math.abs(rotAngleDeg)<0.05 then rotAngleDeg=0 end if housingDepth>0 and rotAngleDeg==0 then -- Added 12/2020: Use different code for housed DT-mortise local rabbetShoulder=0 local seatDepth=0 local housingWidth=0 local dummy, mirrory dummy,mirrory=BvnGetSide(logFace) --toLog(string.format("mirrory=%s", tostring(mirrory))) if not mirrory then nBvnSide=-nBvnSide end BvnAddLine( xc, string.format( "%s 1723 %02d %8.0f 0 %7.0f %7.0f %7.0f %7.0f %7.0f %7.0f %7.0f %7.0f 000 ", sPieceNum, nBvnSide, 10000*xc, 10000*rabbetShoulder, 10000*seatDepth, 10000*tenonWidth, 10000*borderBot, 10*angleDove, 10000*housingDepth, 10000*housingWidth, 10000*tenonLen) ) else BvnAddLine( xc, string.format( "%s 1702 %02d %8.0f %7.0f 0 %s %7.0f %7.0f %7.0f %7.0f %7.0f %7.0f 000 ", sPieceNum, nBvnSide, 10000*xc, 10000*borderBot, s, 10000*tenonWidth, 10000*tenonHeight, 10*angleDove, 10000*housingDepth, 10*rotAngleDeg, 10000*tenonLen ) ) end end end return end tenonLen=tenonLen+housingDepth -- Ensimm�inen tapaus: Suora ty�st� koko hirteen (kantikas tasku) if borderBot<0.001 and borderBot+tenonHeight>faceHeight-0.001 and roundingRadius<0.001 and rotAngleDeg==0 then if logFace>4 then -- L�piura hirren p��h�n --BvnAddGro(sPieceNum, x1, y1, x2, y2, groDepth, groWidth, groAngle, groFace, groAngleLen) BvnAddGro(sPieceNum, midPos+tenonWidth*0.5, 0, midPos+tenonWidth*0.5, faceHeight, tenonLen, tenonWidth, 0, logFace, 0) else BvnAddLine( xc, string.format( "%s 0300 %02d %8.0f 0 0 %7.0f 0 %7.0f %7.0f 0 0 0 000 ", sPieceNum, nBvnSide, 10000*(xc-tenonWidth*0.5), 10000*tenonWidth, 10000*tenonLen, 10000*tenonLen ) ) end return end if roundingRadius<0.001 then AddCncErr( midPos, "BVN WARNING: SQUARE JOINT MADE ROUND ***" ) end -- Tappiliitos -- Ovaali: "000903 0503 03 16521 1855 0 1710 690 1180 1800 0 0 0 000 " -- Py�r: "000903 0503 03 16521 1855 0 1710 690 1180 1800 -10 0 0 000 " -- Eikun t�ll� loveus k��nnetyll� saadaan vain liev�sti py�ristetyt kulmat: -- Tuossa samaan paikaan osuva tappitasikulla 0503 ja loveus k��nnetty: -- "000013 0503 02 9600 1935 0 1460 680 1050 1800 -10 0 0 000 " -- "000013 0305 02 9075 1205 0 1460 1050 680 0 10 10 900 000 " if rotAngleDeg==0 and not sRoundType then -- Optimize oversized tenon (only if not tak�s joint) y1=borderBot y2=y1+tenonHeight -- T�h�n optimointi: Vedet��n max 40 mm ohi puusta minDist=0.040 if not gbK2Tenon then -- Py�re� ty�st�, pit�� menn� v�hint�� leveyden puolikkaan yli minDist=tenonWidth*0.5 + 0.010 if minDist<0.040 then minDist=0.040 end end if logFace>4 and minDist<0.08 then -- P��ss� piti menn� enemm�n ohi minDist=0.080 end if y1<-minDist then y1=-minDist -- K1 0503 tenon len must be >= width if not gbK2Tenon and y2-y1faceHeight+minDist then y2=faceHeight+minDist if not gbK2Tenon and y2-y14 then -- Alkuun/loppuun if math.abs(rotAngleDeg)>0.5 then AddCncErr( x, string.format("ERROR: OLD MORTISE END ROTATION NOT SUPPORTED - SKIPPED (rot angle=%.3f) ***", rotAngleDeg) ) return end if gnBvnLogDir==EBvnDirBeg then xc=gnCurrWidth-xc end --toLog( string.format("x1=%f y1=%f x2=%f y2=%f", x1, y1, x2, y2) ) if logFace==5 then nBvnSide=BvnGetLogBegBvnSide(3) ux=BvnGetXc(0) else nBvnSide=BvnGetLogEndBvnSide(3) ux=BvnGetXc(gnCurrTotLen) end BvnAddLine( xc, string.format( "%s 0504 %02d %8.0f %7.0f 0 900 %7.0f %7.0f %7.0f %7s %7.0f 0 000 ", sPieceNum, nBvnSide, 10000*ux, 10000*xc, 10000*tenonWidth, 10000*tenonLen, 10000*borderBot, sRoundType, 10000*tenonHeight ) ) else -- Calc mid pos in original coordinate system (positive rot angle always ccw) a=rotAngleDegOrg*PI/180.0+PI2 xc=midPos + tenonHeight*0.5*math.cos(a) yc=borderBot + tenonHeight*0.5*math.sin(a) xc=BvnGetXc(xc) if bMirrorY then yc=faceHeight-yc end BvnAddLine( xc, string.format( "%s 0503 %02d %8.0f %7.0f 0 %7.0f %7.0f %7.0f %7.0f %7s 0 0 000 ", sPieceNum, nBvnSide, 10000*xc, 10000*yc, 10000*tenonHeight, 10000*tenonLen, 10000*tenonWidth, 10*rotAngleDeg, sRoundType ) ) end -- T�ss� kone valittaa korkeussuunnassa liian suuresta: -- BvnAddLine( xc, string.format( "%s 0502 %02d %8.0f %7.0f 0 %7.0f %7.0f %7.0f %s 0 0 000 ", -- sPieceNum, nBvnSide, 10000*(xc-tenonWidth*0.5), 10000*(y1 + y2)*0.5, 10000*tenonWidth, 10000*tenonLen, 10000*(y2-y1), sRound ) ) 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 BvnAddBuckGro( sPieceNum, nBvnSide, groStart, groOtherSideOff, buckWidth, buckDepth, openingDepth ) local x1, x2 -- Now forget about groOtherSideOff - just calc x1,x2 if groOtherSideOff<0 then x1=groStart+groOtherSideOff x2=groStart+0.020 -- Oversize to get rid of roundings else x1=groStart-0.020 x2=groStart+groOtherSideOff end -- 000001 0800 04 4220 1040 0 1640 0 80 0 0 0 0 000 --BvnAddLine( x1, string.format( "%s 0800 %02d %8.0f %7.0f 0 %7.0f %7.0f %7.0f 0 0 0 0 000 ", -- sPieceNum, nBvnSide, 10000*x1, 10000*gnCurrWidth*0.5, 10000*buckDepth, 10000*openingDepth, 10000*buckWidth) ) -- 000015 0305 04 67680 1550 0 400 500 725 1800 10 10 900 000 BvnAddLine( x1, string.format( "%s 0305 %02d %8.0f %7.0f 0 %7.0f %7.0f %7.0f 1800 10 10 900 000 ", sPieceNum, nBvnSide, 10000*x2, 10000*(gnCurrWidth+buckWidth)*0.5, 10000*buckWidth, 10000*(x2-x1), 10000*openingDepth) ) 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 BvnAddOpening( sPieceNum, logFace, nDepth, x1, x2, bUseCutter ) local sawLen, groLen, groFace, sawFace, mirrorSawY, nSplinter, xBeg, xEnd, a, yOrg, nLeftRight, nLeftRightOrg local groBeg, groEnd if x1<0.001 then x1=0 end if x2>gnCurrTotLen-0.001 then x2=gnCurrTotLen end if x1<0.001 and x2>gnCurrTotLen-0.001 then -- Whole piece is cut sawFace=BvnGetSide(logFace) BvnAddLine( gnCurrTotLen+100, string.format( "%s 0102 %02d 0 %7.0f 0 0 0 %7.0f 0 0 0 0 000 ", sPieceNum, sawFace, 5000*gnCurrWidth, 10000*(gnProfHeight-nDepth)) ) return end -- Ei koko hirren mittainen, jyrsit��n -- "000001 1500 01 13000 910 0 8000 200 1820 10 0 0 0 000 " --nInOut=0 -- 0=inside, 10=outside --BvnAddLine( x1, string.format( "%s 1500 %02d %8.0f %7.0f 0 %7.0f %7.0f %7.0f %7.0f 0 0 0 000 ", -- sPieceNum, groFace, 10000*x1, 5000*gnCurrWidth, 10000*(x2-x1), 10000*nDepth, 10000*gnCurrWidth, nInOut ) ) groFace=BvnGetSide(logFace) -- 100 is splinter free, 0=not nSplinter=100 -- Cutter length depends on the plank thickness a=math.asin((gnBvnSawR-gnBvnSawMaxDepth)/gnBvnSawR) xBeg=math.cos(a)*gnBvnSawR xEnd=0 if gnCurrWidthgnCurrTotLen-0.001 then groEnd=0 end if bUseCutter==false or x2-x1>(groBeg+groEnd)*groLen then -- Sawing to the middle (speed-up) if logFace==1 then y=gnProfHeight-nDepth nLeftRight=0 else y=nDepth nLeftRight=10 end yOrg=y nLeftRightOrg=nLeftRight sawFace,mirrorSawY=BvnGetSide(2) -- To log's front side if mirrorSawY then y=gnProfHeight-y if nLeftRight==10 then nLeftRight=0 else nLeftRight=10 end end BvnAddLine( x1, string.format( "%s 0109 %02d %8.0f %7.0f 0 %7.0f %7.0f %7.0f 0 10 %7.0f 0 000 ", sPieceNum, sawFace, 10000*(x1-(1-groBeg)*groLen), 10000*y, 10000*(x2+(1-groEnd)*groLen), 10000*y, 10000*gnBvnSawMaxDepth, nLeftRight) ) if gnCurrWidth>gnBvnSawMaxDepth then -- Double groove (hope that gnCurrWidth<2*gnBvnSawMaxDepth) y=yOrg nLeftRight=nLeftRightOrg sawFace,mirrorSawY=BvnGetSide(4) -- To log's back side if mirrorSawY then y=gnProfHeight-y if nLeftRight==10 then nLeftRight=0 else nLeftRight=10 end end BvnAddLine( x1, string.format( "%s 0109 %02d %8.0f %7.0f 0 %7.0f %7.0f %7.0f 0 10 %7.0f 0 000 ", sPieceNum, sawFace, 10000*(x1-(1-groBeg)*groLen), 10000*y, 10000*(x2+(1-groEnd)*groLen), 10000*y, 10000*gnBvnSawMaxDepth, nLeftRight) ) end if groLen>0 and bUseCutter then -- Gro beg if groBeg==1 then BvnAddLine( x1, string.format( "%s 0300 %02d %8.0f 0 0 %7.0f 0 %7.0f %7.0f 0 0 0 %3.0f ", sPieceNum, groFace, 10000*x1, 10000*groLen, 10000*nDepth, 10000*nDepth, nSplinter) ) end -- Gro end if groEnd==1 then BvnAddLine( x1, string.format( "%s 0300 %02d %8.0f 0 0 %7.0f 0 %7.0f %7.0f 0 0 0 %3.0f ", sPieceNum, groFace, 10000*(x2-groLen), 10000*groLen, 10000*nDepth, 10000*nDepth, nSplinter) ) end end elseif groLen>0 then -- Single groove BvnAddLine( x1, string.format( "%s 0300 %02d %8.0f 0 0 %7.0f 0 %7.0f %7.0f 0 0 0 %3.0f ", sPieceNum, groFace, 10000*x1, 10000*(x2-x1), 10000*nDepth, 10000*nDepth, nSplinter) ) end if bUseCutter and groLen>0 and nDepth>gnProfHeight*0.75 then AddCncErr( orgX1, "WARNING, OPENING CUTS MORE THAN 75% OF THE LOG. SAVED TO BVN FILE." ) end 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 BvnAddSaw( sPieceNum, logFace, xStart, dWidth, dDepthTop, dYoffTop, dAngleY2Deg, dAngleY1Deg ) local nBvnSide, bMirrorY, x1Real, x2Real, temp local nLen, nArea, nSplinter, dzNow, dyFace, dyFace2, nSingleCut, yc if logFace>4 then AddCncErr( xStart, string.format("BvnAddSaw, BAD FACE: %d. MACHINING SKIPPED", logFace) ) return end nBvnSide,bMirrorY=BvnGetSide(logFace) nSplinter=100 -- Miten osuu puuhun x1Real=BvnGetXc(xStart) if dWidth==0 then x2Real=BvnGetXc(gnCurrTotLen) else x2Real=BvnGetXc(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 nLen=0 if x1Real<0.001 and x2Real>gnCurrTotLen-0.001 then nArea=0 -- Kaikki elseif x1Real<0.001 then nArea=10 -- To len nLen=x2Real elseif x2Real>gnCurrTotLen-0.001 then nArea=20 -- From len nLen=x1Real else -- Use cutter local d1, d2, nTan if dAngleY1Deg+dAngleY2Deg<-0.1 then AddCncErr( xStart, string.format("BvnAddSaw, VALLEY CUT AT MIDDLE OF THE PLANK - CANNOT DO WITH CUTTER. MACHINING SKIPPED") ) return end if nSingleCut~=2 then -- Cut y1 nTan=math.tan(dAngleY1Deg*PI/180) d1=dDepthTop+yc*nTan d2=d1-dyFace*nTan BvnAddLine( x1Real, string.format( "%s 0302 %02d %8.0f 0 0 %7.0f %7.0f %7.0f %7.0f 0 0 0 %3.0f ", sPieceNum, nBvnSide, 10000*x1Real, 10000*(x2Real-x1Real), 10000*d1, 10000*d2, -10*dAngleY1Deg, nSplinter) ) end if nSingleCut~=1 then -- Cut y2 nTan=math.tan(dAngleY2Deg*PI/180) d1=dDepthTop-yc*nTan d2=d1+dyFace*nTan BvnAddLine( x1Real, string.format( "%s 0302 %02d %8.0f 0 0 %7.0f %7.0f %7.0f %7.0f 0 0 0 %3.0f ", sPieceNum, nBvnSide, 10000*x1Real, 10000*(x2Real-x1Real), 10000*d1, 10000*d2, 10*dAngleY2Deg, nSplinter) ) end nArea=-1 end if nArea~=-1 then local sortPos sortPos=x1Real if nArea==0 then sortPos=gnSortPosLast end if nSingleCut~=0 or dAngleY1Deg+dAngleY2Deg>-0.1 then -- Hip ridge cut or single BvnAddLine( sortPos, string.format( "%s 0102 %02d %8.0f %7.0f 0 %7.0f %7.0f %7.0f %7.0f 0 0 0 000 ", sPieceNum, nBvnSide, 10000*nLen, 10000*yc, 10*dAngleY1Deg, 10*dAngleY2Deg, 10000*(dzNow-dDepthTop), nArea) ) else -- Valley cut BvnAddLine( sortPos, string.format( "%s 0102 %02d %8.0f %7.0f 0 %7.0f %7.0f %7.0f %7.0f 0 0 0 000 ", sPieceNum, nBvnSide, 10000*nLen, 10000*yc, 10*dAngleY1Deg, 10*dAngleY2Deg, 10000*(dzNow-dDepthTop), nArea) ) end end end function BvnMarkReinforcement( sPieceNum, groFace, xMid, yMid, dx, dy, xPosText, yPosText, fontSize, textOff, s ) local xOff, bDone, x if gbBvnAddMarkLine==false then return end -- NO TEXT IN BVN: BvnAddMarking(sPieceNum, groFace, xMid, yMid-0.01, xMid, yMid+0.01, xPosText, yPosText, fontSize, textOff, s, false) -- Beg line x=xMid-dx*0.5 if x>0.001 then BvnAddMarking(sPieceNum, groFace, x, 0, x, 1, 2, 2, 0, 0, "", true) end -- End x=xMid+dx*0.5 if x1 then gbBegCut=false end else endCuts=endCuts+1 if endCuts>1 then gbEndCut=false end 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 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 if nType==EMcFrAngledEndTenon then nBvnSide=BvnGetLogEndBvnSide(3) nAngleSideDeg=-nAngleSideDeg nAngleTopDeg=-nAngleTopDeg else nBvnSide=BvnGetLogBegBvnSide(3) end BvnAddBegEndTenon( sPieceNum, nBvnSide, bDove, tenonLen, tenonWidth, tenonHeight, borderBot, nAngleSideDeg, nAngleTopDeg, ac_objectget("iMc", i, 6), ac_objectget("iMc", i, 12) ) elseif nType==EMcFrAngledBegTenonMort or nType==EMcFrAngledEndTenonMort then --// As angled cut: coordinate of the reference point --// For female, this is to surface and for male this is to the start of the tenon (lower mid) --double dAngleDeg; // [2] Angle as in saw cut, 90 deg=straight cut. Note: Hundegger has swapped angle and bevel from compared to sawing. --double dBevelDeg; // [3] Blade as in saw cut, 90 deg=straight cut --double dX; // [4] Plane's origin point on the anchor side. In plank coordinates instead of cut plane (as in operation) --double dY; // [5] Orientation at base/bottom surface: y positive moves left looked from plank beg --double dZ; // [6] -- --// Tenon/mortise params --double dAnchorSide; // [7] 1...4 to be origin of the coordinate system (first angle, then bevel) --double dJointType; // [8] ETypeXXX: 1=tenon, 2=dt, 3=mortise, 4=dt-pocket, 5=Honka dove, 6=Honka mortise --double dRotAngleDeg; // [9] Tenon rotate angle from the ref point looking at tenon's dir: positive ccw -- --double dLen; // [10] Tenon or mortise depth --double dWidth; // [11] Its width --double dHeight; // [12] And height --double dRoundingRadius; // [13] Ty�kalun py�ristyss�de. 0=Ei ole py�ristetty --double dAngleTenonDeg; // [14] Ainakin lohenpyrst�ss� on alareuna usein kapeampi kuin yl�reuna local isBeg, nSide, nEndType, nRotAngleDeg, roundingRadius, nAngleTenonDeg, housing, xErr nAngleTopDeg=ac_objectget("iMc", i, 2) nAngleSideDeg=ac_objectget("iMc", i, 3) -- bevel x =ac_objectget("iMc", i, 4) y =ac_objectget("iMc", i, 5) z =ac_objectget("iMc", i, 6) nSide =ac_objectget("iMc", i, 7) nEndType =ac_objectget("iMc", i, 8) nRotAngleDeg=ac_objectget("iMc", i, 9) tenonLen =ac_objectget("iMc", i, 10) tenonWidth =ac_objectget("iMc", i, 11) tenonHeight =ac_objectget("iMc", i, 12) roundingRadius =ac_objectget("iMc", i, 13) nAngleTenonDeg =ac_objectget("iMc", i, 14) gMortHousing=0 -- Use global since there are so many functions in between s=FindKeyVal(ac_objectget("iMcStr", i, 1), "housing") if s then gMortHousing=tonumber(s) end b=false if math.abs(nAngleTopDeg-90)<0.1 and math.abs(nAngleSideDeg-90)<0.1 then b=true end if nType==EMcFrAngledBegTenonMort then if b then gbBegCut=true end isBeg=true xErr=0 else if b then gbEndCut=true end isBeg=false xErr=gnCurrTotLen end if nAngleTenonDeg<0 then -- 11/2021: It is straight DT having roundings at top and bottom BvnAddBegEndTenonMortNew( sPieceNum, isBeg, nAngleTopDeg, nAngleSideDeg, x, y, z, nSide, nEndType, nRotAngleDeg, tenonLen, tenonWidth, 0, roundingRadius, 0 ) -- tenonHeight 0 here if math.abs(nRotAngleDeg)>0.1 then AddCncErr( xErr, string.format("ERROR: ROTATED DT WITH DOUBLE ROUNDING NOT SUPPORTED, WRITTEN AS STRAIGHT") ) end if math.abs(nAngleTopDeg-90)>0.1 or math.abs(nAngleSideDeg-90)>0.1 then AddCncErr( xErr, string.format("ERROR: ANGLED DT WITH DOUBLE ROUNDING NOT SUPPORTED, SKIPPING WRITING THE OTHER END") ) else -- Anchor to opposite side nSide=nSide+2 if nSide>4 then nSide=nSide-4 end -- Adjust coordinates local dy dy=gnCurrHeight if nSide==2 or nSide==4 then dy=gnCurrWidth end y=-y z=dy-(z+tenonHeight) BvnAddBegEndTenonMortNew( sPieceNum, isBeg, nAngleTopDeg, nAngleSideDeg, x, y, z, nSide, nEndType, 0, tenonLen, tenonWidth, 0, roundingRadius, 0 ) -- tenonHeight 0 here end else BvnAddBegEndTenonMortNew( sPieceNum, isBeg, nAngleTopDeg, nAngleSideDeg, x, y, z, nSide, nEndType, nRotAngleDeg, tenonLen, tenonWidth, tenonHeight, roundingRadius, nAngleTenonDeg ) end elseif nType==EMcJointBeg or nType==EMcJointEnd then --double dType; // 1, EMcJointBeg/EMcJointEnd --double dSideOneBased; // 2, 1-4 --double dX; // 3, distance from end to furthest point (usually zero) --double dLen; // 4, A: Length of angled part --double dStraightHeight; // 5, B --double dDrillDia; // 6, In meters (UNUSED) --double dDrillCount; // 7 (UNUSED) --double dFlags; // 8, bit0 & bit1 & bit2 = type 0...7: 0=Angled scarf, 1=toothed --double dDrillDist; // 9, distance of closest drillings at middle from each other, 0=space evenly (UNUSED) local isBeg, nSide, len, straight, jointType if nType==EMcJointBeg then gbBegCut=true isBeg=true else gbEndCut=true isBeg=false end nSide =ac_objectget("iMc", i, 2) x =ac_objectget("iMc", i, 3) len =ac_objectget("iMc", i, 4) straight =ac_objectget("iMc", i, 5) jointType =ac_objectget("iMc", i, 8) % 8 BvnAddBegEndJoint( sPieceNum, isBeg, x, nSide, len, straight, jointType ) 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) -- V-Cut does not work with straight (0 degree) and negative angles, try to simulate a V-Cut with other machinings in those cases if bevelDegL<0.0 or bevelDegR<0.0 then BvnAddVCutUsingDoubleDaboSawCuts(sPieceNum, isBeg, nSide, x, y, bevelDegL, bevelDegR) return end local zeroAngle=nil local otherAngle=nil if math.abs(bevelDegL)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 BvnAddMarking(sPieceNum, groFace, x1, y1, x2, y2, xPosText, yPosText, fontSize, 0, s, false, bInLineDir) end elseif nType==EMcFrReinforce then -- Marker lines to beg & end 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-gnBvnYoffset 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 BvnMarkReinforcement( 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 BvnMarkReinforcement( sPieceNum, groFace, x1, y1, dx, dy, xPosText, yPosText, fontSize, textOff, s ) end elseif nType==EMcFrSaw then -- Lengthwise sawing logFace =ac_objectget("iMc", i, 2) xStart =ac_objectget("iMc", i, 3) 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) if logFace<5 then xStart=xStart+begExtra end BvnAddSaw( sPieceNum, logFace, xStart, dWidth, dDepthTop, dYoffTop, dAngleY2Deg, dAngleY1Deg ) elseif nType==EMcFrTenonSide then --! ### 400: Tappiliitos tai lohari kylkeen --double dType; // [1] -- --double dMidPos; // [2] Keskipiste kyljess� (x) --double dBotLevel; // [3] Korkeusasema suhteessa alareunaan (y) -- --// Because changed --double dTenonLen; // [4] Tapin pituus --double dTenonWidth; // [5] --double dTenonHeight; // [6] --double dBottomGapOrRotDeg; // [7] Tapin pohjav�lys, if dFlags bit0=1 this is rotate angle in degrees. Rotate center is mortise bottom mid (x,y) --double dBorderGapOrDtHousingDepth; // [8] Tapin sivuv�lys, if dFlags bit0=1 (from 2014) this is housing depth for mortise (not included in tenon len) -- --double dRoundingRadius; // [9] -- --double dJointSideOneBased; // [10] IFramePlankHandler::SideType+1: 1...6 --double dJointType; // [11] ETypeXXX -- --double dAngleTenonDeg; // [12] Ainakin lohenpyrst�ss� on alareuna usein kapeampi kuin yl�reuna --double dFlags; // [13] Version control, bit0 used local logFace, str, bDove, midPos, borderBot, tenonLen, tenonWidth, tenonHeight, sideGap, bottomGap, angleDove, roundingR, rotAngleDeg, isNew, housingDepth isNew =ac_objectget("iMc", i, 13) % 1 -- bit0: Version control 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) rotAngleDeg=0 bottomGap=0 sideGap=0 housingDepth=0 if isNew then rotAngleDeg =ac_objectget("iMc", i, 7) housingDepth=ac_objectget("iMc", i, 8) else bottomGap =ac_objectget("iMc", i, 7) sideGap =ac_objectget("iMc", i, 8) end roundingR =ac_objectget("iMc", i, 9) logFace =ac_objectget("iMc", i, 10) angleDove =ac_objectget("iMc", i, 12) if logFace<5 then midPos=midPos+begExtra end BvnAddSideJoint( sPieceNum, logFace, bDove, midPos, borderBot, tenonLen+bottomGap, tenonWidth+sideGap*2, tenonHeight, angleDove, roundingR, rotAngleDeg, housingDepth ) 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 midPos =ac_objectget("iMc", i, 2)+begExtra width =ac_objectget("iMc", i, 3) depthTop =ac_objectget("iMc", i, 4) depthBot =ac_objectget("iMc", i, 5) depthSides =ac_objectget("iMc", i, 6) BvnAddBalkMid(sPieceNum, midPos, width, depthTop, depthBot, depthSides) elseif nType==EMcFrBeamFemale then local side, width, height, openside, subtype x1 =ac_objectget("iMc", i, 2) y1 =ac_objectget("iMc", i, 3) side =ac_objectget("iMc", i, 4) width =ac_objectget("iMc", i, 5)-0.078 openside=ac_objectget("iMc", i, 6) subtype =ac_objectget("iMc", i, 8) x1=x1+begExtra height=width if side==0 and subtype==0 and (openside==1 or openside==3) then -- Tak�s through the log: x1,y1 is the middle of the tak�s if openside==1 and y10 then height=height+y1 y1=y1-y1/2 -- Should actually add to gnProfHeight and add double to height (similar to Pro Conv) end --ac_environment("tolog", string.format("width=%f height=%f y1=%f", width, height, y1)) BvnAddSideJoint( sPieceNum, 2, false, x1, y1-height/2, 0, width, height, 0, width/2, 0, 0, " 0" ) --BvnAddSideJoint( sPieceNum, logFace, bDove, midPos, borderBot, tenonLen, tenonWidth, tenonHeight, angleDove, roundingRadius, rotAngleDeg, housingDepth, sRoundType ) else AddCncErr( x1, string.format("UNSUPPORTED BEAM MORTISE JOINT SKIPPED (subtype=%d)", subtype) ) end elseif nType==EMcFrNailGroup or nType==EMcFrNailLine or nType==EMcFrBalkShoe then -- NOP for Hundegger on purpose elseif nType~=0 then AddCncErr( ac_objectget("iMc", i, 2), string.format("UNSUPPORTED MACHINING %d", nType) ) end end -- Lengthwise profile local tblBeg, tblMid, tblEnd tblBeg=ProfPolyToEdges("iMatProfileXzBeg", gnCurrHeight) tblMid=ProfPolyToEdges("iMatProfileXzMid", gnCurrHeight) tblEnd=ProfPolyToEdges("iMatProfileXzEnd", gnCurrHeight) if tblEnd then ProfPolyToEnd(tblEnd, gnCurrTotLen) end if tblBeg~=nil then s=InsertProfTbl(sPieceNum, tblBeg, 0, false, gnCurrHeight ) BvnAddLine(BvnGetXc(0), s, true) 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 s=InsertProfTblMid(sPieceNum, tblMid, gnCurrHeight, x1, x2) BvnAddLine(BvnGetXc(x1), s, true) end if tblEnd~=nil then s=InsertProfTbl(sPieceNum, tblEnd, 0, true, gnCurrHeight) BvnAddLine(BvnGetXc(gnCurrTotLen), s, true) end 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 ProfPolyToEdges(parName, faceHeight) local n, i, v, prevx, prevy, midx, midy, x2, y2, status, dest local tbl, tblBox, tblEdges n=ac_objectget(parName,-1) if not n or 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 ProfPolyToEnd(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 IsPlankContour(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 function AngleTo2pi(a) local neg, pos while true do if a<0 then if pos then break end a=2*PI + a neg=true elseif a>2*PI then if neg then break end a=a-2*PI pos=true else break end end return a end -- Checks if points ix1,iy1 or ix2,iy2 are inside v.x1,v.y1 -> v.x2,v.y2 function PointsInLine(v,ix1,iy1,ix2,iy2) local off, dist, len if ix1 then off,dist,len=ac_geo("linedist", v.x1, v.y1, v.x2, v.y2, ix1, iy1) --toLog(string.format("line1: v.x1=%f, v.y1=%f, v.x2=%f, v.y2=%f, ix1=%f, iy1=%f, off=%f dist=%f len=%f", v.x1, v.y1, v.x2, v.y2, ix1, iy1, off, dist, len)) if dist>0.0005 and dist0.0005 and disttblPoly.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 IsPlankContour(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, isBeg, tbl, sCnc, x, y, len, x2, y2, a, nBvnSide, bMirrorY, prevDir, nEnd, vprev, vnext sCnc="" isBeg=true nBvnSide,bMirrorY=BvnGetSide(2) prevDir=0 -- Angle after prev segment in bvn-coordinates, always positive in radians vprev=nil -- Previous segment for i,v in ipairs(tblAdd) do if isBeg then -- Add starting point --000003 3000 -3 4000 2040 0 0 0 0 000 --000003 3001 -3 4000 2040 0 20 0 200 0 20 0 30000 000 x=BvnGetXc(v.x1) y=BvnMirrorYc(v.y1, 2, bMirrorY) s=string.format( "%s 3000 %02d %8.0f %7.0f 0 0 0 0 000 \n" .. "%s 3001 %02d %8.0f %7.0f 0 10 0 200 0 20 0 0 000 ", -- 20=tool on right from the line (10=left), 200=speed (mm/sec), 20=Tool type sPieceNum, BvnGetSide(2), x*10000, y*10000, sPieceNum, BvnGetSide(2), x*10000, y*10000 ) if sCnc~="" then s="\n" .. s -- Not first contour end sCnc=sCnc .. s end vnext=nil nEnd=0 if i==#tblAdd or v.edgeindex+1~=tblAdd[i+1].edgeindex then -- Terminate path nEnd=1 else vnext=tblAdd[i+1] -- Next segment end s=nil if v.isarc then -- Arc segment -- 000003 3005 03 5808.1 -342.1 1230 0 0 -870 930 0 0 0 000 local r, a2 -- Calc r x=v.x1-v.midx y=v.y1-v.midy r=math.sqrt(x*x + y*y) -- Calc begin angle as vector in bvn-coords x=math.cos(v.anglebegrad+PI2) -- Tangent of the circle in this angle if gnBvnLogDir==EBvnDirEnd then x=-x end y=math.sin(v.anglebegrad+PI2) if bMirrorY then y=-y end a=AngleTo2pi(math.atan2(y, x)) if v.anglelenrad<0 then a=a+PI end a=a-prevDir a=AngleTo2pi(a) a2=v.anglelenrad if bMirrorY then a2=-a2 end if gnBvnLogDir==EBvnDirEnd then a2=-a2 end x2=BvnGetXc(v.x2) y2=BvnMirrorYc(v.y2, 2, bMirrorY) s=string.format( "\n%s 3005 %02d %8.0f %7.0f %7.0f 0 0 %7.0f %7.0f 0 0 0 %03d ", sPieceNum, BvnGetSide(2), x2*10000, y2*10000, r*10000, a/PI*1800, a2/PI*1800, nEnd ) sCnc=sCnc .. s prevDir=prevDir+a+a2 if prevDir<0 then prevDir=2*PI + prevDir elseif prevDir>=2*PI then prevDir=prevDir-2*PI end else -- Line segment -- 000003 3002 -3 2787.6 1340 0 0 0 3300 1400 0 0 0 000 local ix1, iy1, ix2, iy2 -- Intersection coords local dx, dy, offbeg, offend -- Unit vector for current line dx=v.x2-v.x1 dy=v.y2-v.y1 len=math.sqrt(dx*dx+dy*dy) dx=dx/len dy=dy/len offbeg=0 offend=0 if vprev then -- Adjust line beg with previous segment, it ends to this point's begin point -- Move so that finger cutter does not cut previous arc if vprev.isarc then local cx, cy cx=vprev.x1-vprev.midx cy=vprev.y1-vprev.midy r=math.sqrt(cx*cx + cy*cy) while true do ix1,iy1,ix2,iy2=GetCirclesX(vprev.midx, vprev.midy, r, v.x1+offbeg*dx + millRadius*dy, v.y1+offbeg*dy - millRadius*dx, millRadius, vprev.anglebegrad, vprev.anglelenrad) if ix2==nil or offbeg+0.001>len then -- No intersection (max touching prev segment) or segment will be empty break end -- Advance by 1 mm offbeg=offbeg+0.001 end else -- Line segment while true do ix1,iy1,ix2,iy2=GetLineCircleX(vprev.x1, vprev.y1, vprev.x2, vprev.y2, v.x1+offbeg*dx + millRadius*dy, v.y1+offbeg*dy - millRadius*dx, millRadius) if ix1==nil or offbeg+0.001>len or not PointsInLine(vprev,ix1,iy1,ix2,iy2) then -- No intersection (max touching prev segment) or segment will be empty break end -- Advance by 1 mm offbeg=offbeg+0.001 end end end -- Adjust line end with next segment if vnext then -- Adjust line beg with previous segment, it ends to this point's begin point -- Move so that finger cutter does not cut previous arc if vnext.isarc then local cx, cy cx=vnext.x1-vnext.midx cy=vnext.y1-vnext.midy r=math.sqrt(cx*cx + cy*cy) while true do ix1,iy1,ix2,iy2=GetCirclesX(cx, cy, r, v.x2-offend*dx + millRadius*dy, v.y2-offend*dy - millRadius*dx, millRadius, vnext.anglebegrad, vnext.anglelenrad) if ix2==nil or offend+0.001>len then -- No intersection (max touching prev segment) or segment will be empty break end -- Advance by 1 mm offend=offend+0.001 end else -- Line segment while true do ix1,iy1,ix2,iy2=GetLineCircleX(vnext.x1, vnext.y1, vnext.x2, vnext.y2, v.x2-offend*dx + millRadius*dy, v.y2-offend*dy - millRadius*dx, millRadius) --if ix1==nil or offend+0.001>len then if ix1==nil or offend+0.001>len or not PointsInLine(vnext,ix1,iy1,ix2,iy2) then -- No intersection (max touching prev segment) or segment will be empty break end -- Advance by 1 mm offend=offend+0.001 end end end x=BvnGetXc(v.x1) y=BvnMirrorYc(v.y1, 2, bMirrorY) x2=BvnGetXc(v.x2) y2=BvnMirrorYc(v.y2, 2, bMirrorY) dx=(x2-x)/len -- Now to bvn vector dy=(y2-y)/len a=math.atan2(y2-y, x2-x) if a<0 then a=2*PI + a end if offbeg+offend+0.001>len then -- Whole segmen skipped, move to last position s=string.format( "\n%s 3008 %02d %8.0f %7.0f 0 0 0 %7.0f %7.0f 0 0 0 %03d ", sPieceNum, BvnGetSide(2), x2*10000, y2*10000, AngleTo2pi(a-prevDir)/PI*1800, len*10000, nEnd ) sCnc=sCnc .. s prevDir=a else if offbeg>EPS then -- Move cutter without cutting s=string.format( "\n%s 3008 %02d %8.0f %7.0f 0 0 0 %7.0f %7.0f 0 0 0 000 ", sPieceNum, BvnGetSide(2), (x+offbeg*dx)*10000, (y+offbeg*dy)*10000, AngleTo2pi(a-prevDir)/PI*1800, offbeg*10000 ) sCnc=sCnc .. s prevDir=a end -- Cut s=string.format( "\n%s 3002 %02d %8.0f %7.0f 0 0 0 %7.0f %7.0f 0 0 0 %03d ", sPieceNum, BvnGetSide(2), (x2-offend*dx)*10000, (y2-offend*dy)*10000, AngleTo2pi(a-prevDir)/PI*1800, (len-offbeg-offend)*10000, nEnd ) sCnc=sCnc .. s prevDir=a if offend>EPS then -- Move cutter without cutting s=string.format( "\n%s 3008 %02d %8.0f %7.0f 0 0 0 %7.0f %7.0f 0 0 0 000 ", sPieceNum, BvnGetSide(2), x2*10000, y2*10000, AngleTo2pi(a-prevDir)/PI*1800, offend*10000 ) sCnc=sCnc .. s prevDir=a end end end vprev=v isBeg=false if nEnd==1 then isBeg=true vprev=nil prevDir=0 end end return sCnc end -- Like InsertProfTbl but takes x1,x2 and multiplies polygon on that area & handles top and bottom separately function InsertProfTblMid(sPieceNum, tblPoly, faceHeight, x1, x2) local xoff, i, v, s, sTop, sBot, x2clip sTop="" sBot="" xoff=x1 while x1+0.001 if not sUsage then -- It is a plank, set defaults as they were sUsage=ac_objectget("iUsageId") if not sUsage then sUsage="" -- Board obj end s=ac_objectget("iMatId") if s~=string.format("%.0fx%.0f", gnCurrWidth*1000, gnCurrHeight*1000) then sComment=s end end -- part number from ID, if it is number only if string.find(gsCurrentLogId, "[^%d]")==nil and gbBvnForceUnid==false then -- Pelkk�� numeroa if gnBvnPieceNum==nil then -- H�t�varana, jos on muitakin kuin pelkki� numeerisia tunnuksia gnBvnPieceNum=100000 end sPieceNum=string.format( "%06s", gsCurrentLogId ) sPart=sUsage if sPart=="" then sPart=gsCurrentLogId end else -- ID:ss� muutakin kuin numeroa if gnBvnPieceNum==nil then gnBvnPieceNum=1 end if sUsage~="" then sPart=string.format( "%s (%s)", gsCurrentLogId, sUsage) else sPart=gsCurrentLogId end sPieceNum=string.format( "%06d", gnBvnPieceNum ) gnBvnPieceNum=gnBvnPieceNum+1 end -- Compatibility for pre 11/2017 (old code) <-- if useInfoSettings then -- New settings from 11/2017 for frame plank local plankinfo plankinfo=af_request("plankinfo") sPart=GetInfoStr(gnBvnInfoPart, plankinfo) sUnit=GetInfoStr(gnBvnInfoUnit, plankinfo) sGrade=GetInfoStr(gnBvnInfoGr, plankinfo) sComment=GetInfoStr(gnBvnInfoComments, plankinfo) sProf=GetInfoStr(gnBvnInfoProf, plankinfo) sRoof=GetInfoStr(gnBvnInfoRoof, plankinfo) sType=GetInfoStr(gnBvnInfoType, plankinfo) if false then -- Debug/test types ac_environment("tolog", string.format("EInfoFullIDPlusUsage=%s", GetInfoStr(EInfoFullIDPlusUsage, plankinfo))) ac_environment("tolog", string.format("EInfoShortID=%s", GetInfoStr(EInfoShortID, plankinfo))) ac_environment("tolog", string.format("EInfoFullID=%s", GetInfoStr(EInfoFullID, plankinfo))) ac_environment("tolog", string.format("EInfoElementID=%s", GetInfoStr(EInfoElementID, plankinfo))) ac_environment("tolog", string.format("EInfoUsage=%s", GetInfoStr(EInfoUsage, plankinfo))) ac_environment("tolog", string.format("EInfoMatIdFull=%s", GetInfoStr(EInfoMatIdFull, plankinfo))) ac_environment("tolog", string.format("EInfoMatIdShort=%s", GetInfoStr(EInfoMatIdShort, plankinfo))) ac_environment("tolog", string.format("EInfoGrade=%s", GetInfoStr(EInfoGrade, plankinfo))) ac_environment("tolog", string.format("EInfoMatIdIfNotSize=%s", GetInfoStr(EInfoMatIdIfNotSize, plankinfo))) ac_environment("tolog", string.format("EInfoPlankRole=%s", GetInfoStr(EInfoPlankRole, plankinfo))) ac_environment("tolog", string.format("EInfoPlankRoleFin=%s", GetInfoStr(EInfoPlankRoleFin, plankinfo))) ac_environment("tolog", string.format("EInfoPlankRoleNor=%s", GetInfoStr(EInfoPlankRoleNor, plankinfo))) end end if gbBvnAddMarkLine and HasReinforce() then if sComment~="" then sComment=sComment .. " " end sComment=sComment .. "REINFORCED" end -- Must be numeric if sRoof=="" then sRoof="0" end -- Headers BvnAddLine( -1010, string.format( "%s %-20.20s %-10.10s%-2.2s%-3.3s%-3.3s%-5.5s%-10.10s%-40.40s ", sPieceNum, sPart, sType, sProf, sRoof, sGrade, sUnit, sGrade, sComment) ) if gnBvnPlateSide==EBvnMale or gnBvnPlateSide==EBvnFemale then x=gnCurrWidth y=gnProfHeight else y=gnCurrWidth x=gnProfHeight end BvnAddLine( -1005, string.format( "%s %6.0f 0 %6.0f %6.0f %6.0f ", sPieceNum, nSimilarCount, 10000*x, 10000*y, 10000*gnCurrTotLen) ) -- Longer/other attributes if string.len(sProf)>2 then BvnAddLine( -1004, string.format( "%s 3200 03 %30.30s ", sPieceNum, sProf) ) end if string.len(sUnit)>5 then BvnAddLine( -1004, string.format( "%s 3201 03 %30.30s ", sPieceNum, sUnit) ) end if string.len(sGrade)>10 then BvnAddLine( -1004, string.format( "%s 3202 03 %30.30s ", sPieceNum, sGrade) ) end return sPieceNum end function BvnCncTblToStr() local s, s2 -- From table to text if gTblCnc==nil then return "" end -- 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) s="" for i,v in ipairs(gTblCnc) do s2 = v.text .. "\n" s=s .. s2 end -- May not add empty line: k2.exe does not like it: s=s .. "\n" return s end -- Write ArchiFrame plank Hundegger K1/K2 code. -- ac_objectopen() must have been called before calling this -- Saves CNC to variable gsCncText function WriteBvnPlank(unidPlank, nSimilarCount) local s, n, i, nOther, x, nDepth, x1, x2, nWidth, sBin, y, z, sPart, sUsage local bBegJoint, bEndJoint, nLeftRight local nBvnSide, bMirrorY local nSplinter local nAngleSide, nStraightSide, nAngleTop, nStraightTop local nSide, sSide, nSortPos, prevSide, prevDir gnCncCount=0 gTblCnc=nil gsCncText="" gsCurrentLogId=ac_objectget("#id") BvnGetPlankBasicParams(unidPlank) prevSide=gnBvnPlateSide if gnBvnAutoRotate==1 then -- Select feeding direction automatically if gnCurrHeight>gnCurrWidth then gnBvnPlateSide=EBvnFemale else gnBvnPlateSide=EBvnFrontSide end end -- Put material ID if it is different to widthxheight sPieceNum=BvnAddPartHeader(nil, nSimilarCount, true) nSplinter=100 if gbBvnNoSplinterFree then nSplinter=0 end -- Frame plank has all machinings in single table including beginning and ending shapes BvnWriteMcTbl(0) if gnPrintInfoSide~=0 and gsPrintInfoContent~="" then -- Print ID local sMark, xMid, yTop, x1, width, forcedFontSize local plankinfo plankinfo=af_request("plankinfo") sMark=gsPrintInfoContent sMark=ac_environment ("strreplace", sMark, "#fullid#", gsCurrentLogId) sMark=ac_environment ("strreplace", sMark, "#projnum#", ac_environment("parsetext", "")) sMark=ac_environment ("strreplace", sMark, "#projid#", ac_environment("parsetext", "")) sMark=ac_environment ("strreplace", sMark, "#shortid#", GetInfoStr(EInfoShortID, plankinfo)) sMark=ac_environment ("strreplace", sMark, "#usage#", GetInfoStr(EInfoUsage, plankinfo)) sMark=ac_environment ("strreplace", sMark, "#plankrole#", GetInfoStr(EInfoPlankRole, plankinfo)) sMark=ac_environment ("strreplace", sMark, "#plankrolefin#", GetInfoStr(EInfoPlankRoleFin, plankinfo)) sMark=ac_environment ("strreplace", sMark, "#plankrolenor#", GetInfoStr(EInfoPlankRoleNor, plankinfo)) sMark=ac_environment ("strreplace", sMark, "#plankroleshort#", GetInfoStr(EInfoPlankRoleShort, plankinfo)) sMark=ac_environment ("strreplace", sMark, "#plankroleshortfin#", GetInfoStr(EInfoPlankRoleShortFin, plankinfo)) sMark=ac_environment ("strreplace", sMark, "#plankroleshortnor#", GetInfoStr(EInfoPlankRoleShortNor, plankinfo)) sMark=ac_environment ("strreplace", sMark, "#ownerelemid#", GetInfoStr(EOwnerElemID, plankinfo)) sMark=ac_environment ("strreplace", sMark, "#floornum#", GetInfoStr(EInfoFloorNum, plankinfo)) yTop=gnCurrHeight -- Adjust so that does not intersect with grooves and markings if gnPrintInfoHeight<0.001 then gnPrintInfoHeight=0.001 end width=string.len(sMark)*gnPrintInfoHeight*0.67 -- 20 mm per character for 30 mm high one width=width+0.040 -- To the beginning xMid=width*0.5 if gnPrintInfoXpos==2 then xMid=gnCurrTotLen*0.5 elseif gnPrintInfoXpos==3 then xMid=gnCurrTotLen-width*0.5 if xMid<0 then xMid=0 end end if gnBvnLogDir==EBvnDirEnd then xMid=gnCurrTotLen-xMid end x1=xMid-width*0.5 if x1<0 then x1=0 end forcedFontSize=0 if gnBvnForceTextSize then forcedFontSize=gnBvnForceTextSize end --toLog(string.format("pre: gnPrintInfoHeight=%f x1=%f width=%f", gnPrintInfoHeight, x1, width)) x1=af_request("mc_findtextpos", gnPrintInfoSide, x1, width, forcedFontSize) --toLog(string.format("pre: gnPrintInfoHeight=%f x1=%f width=%f", gnPrintInfoHeight, x1, width)) local prev=gnBvnForceTextSize gnBvnForceTextSize=nil -- Size: function will multiply by 100 making font size 10 to be 10 mm, here we have meter --BvnAddMarking(sPieceNum, gnPrintInfoSide, x1, 0, x1+width, 0, 1, 2, gnPrintInfoHeight*10000/100, 0, sMark, false) BvnAddMarking(sPieceNum, gnPrintInfoSide, x1, 0, x1+width, 0, 2, 2, gnPrintInfoHeight*10000/100, 0, sMark, false) gnBvnForceTextSize=prev end -- Sawings to beg and end n=3 if gnBvnLogDir==EBvnDirEnd then n=-n end if gbBegCut==false then BvnAddLine( -1001, string.format( "%s 0100 %02d %8.0f %7.0f %7.0f 900 900 0 0 0 0 0 000 ", sPieceNum, n, 10000*BvnGetXc(0), 0, 0) ) end if gbEndCut==false then BvnAddLine( -1001, string.format( "%s 0100 %02d %8.0f %7.0f %7.0f 900 900 0 0 0 0 0 000 ", sPieceNum, -n, 10000*BvnGetXc(gnCurrTotLen), 0, 0) ) end gsCncText = gsCncText .. BvnCncTblToStr() gnBvnPlateSide=prevSide return 1 end ----------------------------------------------------------------------------- -- BTL writer common code -- Which side of the log is against the machine's base plate (not the table, suomeksi "vaste") EBtlMale=0 -- Log top EBtlFrontSide=1 EBtlFemale=2 -- Log bottom EBtlBackSide=3 gnBtlPlateSide=EBtlFrontSide -- Actually which side is towards user in btlviewer -- Which end goes first to the machine EBtlDirBeg=0 EBtlDirEnd=1 gnBtlLogDir=EBtlDirBeg -- Also for AF plank -- Settings gnBtlYoffset=0 -- Value to add to y-coordinate (in faces 2,4) to get current log's/plank's bottom. May be negative if overlap is below the piece. -- Hundegger writer globals gnBtlPieceNum=1 -- Global piece number -- K��nt�� jos menee loppu edell� function BtlGetXc(nCoord) if gnBtlLogDir==EBtlDirBeg then return nCoord end return gnCurrTotLen-nCoord end function BtlMirrorYc(nCoord, logFace) if logFace==1 or logFace==3 then return gnCurrWidth - nCoord end return gnProfHeight - nCoord end -- Returns +-1...4 for the log's beginning head (left side) depending on gnBtlLogDir and side -- Second value: bool to mirror x-coordinates for the end face OK ONLY IF USING FACE 3 (bottom) -- Third value: bool to mirror y-coordinates function BtlGetLogBegBvnSide(logFace) local nBtlSide, mirrorX, mirrorY mirrorX=false mirrorY=false nBtlSide=BtlGetSide14(logFace) if gnBtlLogDir==EBtlDirEnd then error("unsupported feed dir") nBtlSide=-nBtlSide else mirrorX=true end return nBtlSide, mirrorX, mirrorY end -- Returns +-1...4 for the log's end head (right side) depending on gnBtlLogDir and side function BtlGetLogEndBvnSide(logFace) local face, mirrorX, mirrorY face, mirrorX, mirrorY = BtlGetLogBegBvnSide(logFace) return -face, mirrorX, mirrorY end -- For BvnGetSide, first index is ArchiLogs side 1...4 (top, front, bottom, back), second is the log side towards the user in btlviewer (EBtlFemale etc), third is direction EBtlDirBeg/end gBtlSides={ -- log top -- FEEDING END FIRST IS NOT DONE SINCE IT IS NOT SUPPORTED NOW [1] = { [EBtlMale] = { [EBtlDirBeg]={1, false}, [EBtlDirEnd]={4, false} }, [EBtlFrontSide] = { [EBtlDirBeg]={4, false}, [EBtlDirEnd]={3, false} }, [EBtlFemale] = { [EBtlDirBeg]={3, false}, [EBtlDirEnd]={2, false} }, [EBtlBackSide] = { [EBtlDirBeg]={2, false}, [EBtlDirEnd]={1, false} } }, -- log front [2] = { [EBtlMale] = { [EBtlDirBeg]={2, false}, [EBtlDirEnd]={1, false} }, [EBtlFrontSide] = { [EBtlDirBeg]={1, false}, [EBtlDirEnd]={4, false} }, [EBtlFemale] = { [EBtlDirBeg]={4, false}, [EBtlDirEnd]={3, false} }, [EBtlBackSide] = { [EBtlDirBeg]={3, false}, [EBtlDirEnd]={2, false} } }, -- log bottom [3] = { [EBtlMale] = { [EBtlDirBeg]={3, true}, [EBtlDirEnd]={2, true } }, [EBtlFrontSide] = { [EBtlDirBeg]={2, true}, [EBtlDirEnd]={1, true} }, [EBtlFemale] = { [EBtlDirBeg]={1, true}, [EBtlDirEnd]={4, true} }, [EBtlBackSide] = { [EBtlDirBeg]={4, true}, [EBtlDirEnd]={3, true} } }, -- log back [4] = { [EBtlMale] = { [EBtlDirBeg]={4, true}, [EBtlDirEnd]={3, true } }, [EBtlFrontSide] = { [EBtlDirBeg]={3, true}, [EBtlDirEnd]={2, true} }, [EBtlFemale] = { [EBtlDirBeg]={2, true}, [EBtlDirEnd]={1, true} }, [EBtlBackSide] = { [EBtlDirBeg]={1, true}, [EBtlDirEnd]={4, true} } } } -- Returns two values: -- first: bvn side is: 1=top, 2=not against plate, 3=bottom against the table, 4=against the plate -- second: true=need to mirror coordinates in y-direction, false=nope. x-direction must be swapped if gnBtlLogDir==EBtlDirEnd function BtlGetSide14(logFace) return gBtlSides[logFace][gnBtlPlateSide][gnBtlLogDir][1], gBtlSides[logFace][gnBtlPlateSide][gnBtlLogDir][2] end -- BTL BTL BTL BTL BTL BTL BTL BTL BTL BTL BTL BTL BTL BTL BTL BTL BTL -- BTL BTL BTL BTL BTL BTL BTL BTL BTL BTL BTL BTL BTL BTL BTL BTL BTL local function ToBtl(s) s=string.gsub(s, "\"", "") return s end -- Adds cnc position and content to table. Table will be sorted at the end. function BtlAddMc(nPos, sText) local i if nPos==nil then error("BtlAddMc/nPos") end if sText==nil then error("BtlAddMc/sText") end if gnCncCount==0 then gTblCnc={} end gnCncCount=gnCncCount+1 gTblCnc[gnCncCount] = {} gTblCnc[gnCncCount].pos =nPos gTblCnc[gnCncCount].count =gnCncCount gTblCnc[gnCncCount].text =sText end -- THIS IS FOR BACKWARDS COMPATIBILITY & FOR LOG OBJECT FROM 3/2012 -- Side is tilted to front/back face, Top is to top/bottom -- Angles: 90deg=straight. All rules from log object -- xc: in plank's coordinates (total length or 0) -- nBegEnd: +-3 function BtlAddBegEndAngledOld( sPieceNum, nAngleSide, nStraightSide, nAngleTop, nStraightTop, xc, nBegEnd ) AddCncErr( 0, "ERROR: OLD ANGLED CUTS NOT SUPPORTED - SKIPPED" ) end -- Adds angled ending to beginning function BtlAddBegAngledOld( sPieceNum, nAngleSide, nStraightSide, nAngleTop, nStraightTop ) BtlAddBegEndAngledOld( sPieceNum, nAngleSide, nStraightSide, nAngleTop, nStraightTop, 0, 3 ) end function BtlAddEndAngledOld( sPieceNum, nAngleSide, nStraightSide, nAngleTop, nStraightTop ) BtlAddBegEndAngledOld( sPieceNum, nAngleSide, nStraightSide, nAngleTop, nStraightTop, gnCurrTotLen, -3 ) end -- Table gBtlCutAdjustAngleTbl={ [EBtlMale] = { [EBtlDirBeg]=false, [EBtlDirEnd]=true }, [EBtlFrontSide] = { [EBtlDirBeg]=false, [EBtlDirEnd]=false }, [EBtlFemale] = { [EBtlDirBeg]=true, [EBtlDirEnd]=false }, [EBtlBackSide] = { [EBtlDirBeg]=true, [EBtlDirEnd]=true } } -- NEW ANGLED ENDING STARTING FROM 3/2012 FOR iMc -- Angles: As for 101 in plank object -- xoff,yff,zoff: Offset from the plank end refline, y-offset must have been set to zoff -- mcIndex: Machining index 1-based -- Returns isBeg perhaps set to true if moved a cut from end to begin function BtlAddBegEndAngled( sPieceNum, nAngle, nBevel, xoff, yoff, zoff, isBeg, mcIndex ) local xc, yc, nBegEnd, nBtlSide local temp local bMirrorY,bMirrorTopY if isBeg then xc=xoff nBegEnd=2 else xc=gnCurrTotLen-xoff nBegEnd=1 end xc=BtlGetXc(xc) if gnBtlLogDir==EBtlDirEnd then error( "BAD DIR" ) --nAngle=PI-nAngle end -- Anchor to bottom side nBtlSide,bMirrorY=BtlGetSide14(3) --if bMirrorY then --nBevel=PI-nBevel --zoff=gnProfHeight-zoff --end yoff=yoff+gnCurrWidth*0.5 -- 0=back side side, width=front side if bMirrorY then yoff=gnCurrWidth-yoff nAngle=PI-nAngle end -- Check if full plank cut local tblInfo, tblRes tblInfo={} tblInfo.guid = gsCurrentLogGuid tblInfo.mcindex = mcIndex tblRes = af_request("mc_getcutinfo", tblInfo) if tblRes.fullcut==1 or true then -- Request by Norsk Modul 10/2021: Move to begin if at the end and cuts the whole plank if not isBeg and math.abs(nAngle-PI2)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 gnDestMachine ~= EDestTiger and 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() return "" -- Not implemented 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() local sDesignation = "(" .. sPart .. BtlGetIdPostStr() .. ")" -- Just plank ID if tiger if gnDestMachine == EDestTiger then -- Other input here sDesignation = gsCurrentLogId end 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(sDesignation), "", -- ANNOTATION, unidPlank written at transformation part sStorey, ToBtl(sPieceNum), sPackage, sMat, nSimilarCount, gnCurrTotLen*10000.0, x*10000.0, y*10000.0, gnBtlStartEndOffset*10000.0, gnBtlStartEndOffset*10000.0 ) --gnUid ) -- Weinmann & GranIT specific: GUID links the plank group between btl and wup s=s .. string.format("USERATTRIBUTE: \"PROCESS\": \"%d\"\n", ac_objectget("iCnc")) s=s .. string.format("USERATTRIBUTE: \"GUID\": \"%s\"\n", tblSimilarPos[1].guid) 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 -- Weinmann & GranIT specific, add planks to group if gtblPlankGuid2GroupGuid then for i=1,nSimilarCount do gtblPlankGuid2GroupGuid[tblSimilarPos[i].guid]=tblSimilarPos[1].guid end 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.0010.995 then if math.abs(val)<0.049 then s="0" -- Avoid "-0" else s=string.format("%.0f", val) end else s=string.format("%.02f", val) end if string.len(string.format("%.0f", val))>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 -- No decimals function WupLen2StrNoDec(val) local s, off val=val*1000 off=math.abs(val-math.floor(val)) if math.abs(val)<0.049 then s="0" -- Avoid "-0" else s=string.format("%.0f", val) end if string.len(string.format("%.0f", val))>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 -- Single decimal since ArchiFrame adjusts spacing to have single decimal function WupLen2StrNail(val) local s, off val=val*1000 off=math.abs(val-math.floor(val)) if math.abs(val)<0.049 then s="0" -- Avoid "-0" else s=string.format("%.1f", val) end if string.len(string.format("%.1f", val))>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 -- Converts int to wup-length str (max 8 digits for now) function WupInt2Str(val) local s s=string.format("%.0f", val) if string.len(s)>8 then if gnWarnings==nil then gnWarnings=0 end if gnWarnings<100 then toLog( string.format("ERROR: INT-VALUE OUT OF RANGE: %s", s) ) gnWarnings=gnWarnings+1 end end return s end -- Adds cnc position and content to table. Table will be sorted at the end. function WupAddMc(nPos, sText) local i if nPos==nil then error("WupAddMc/nPos") end if sText==nil then error("WupAddMc/sText") end if gnCncCount==0 then gTblCnc={} end gnCncCount=gnCncCount+1 gTblCnc[gnCncCount] = {} gTblCnc[gnCncCount].pos =nPos gTblCnc[gnCncCount].count =gnCncCount gTblCnc[gnCncCount].text =sText end -- Adds one groove in Logs coordinates to bvn-file. Adds notice if not possible. -- sWupCode FRZ/FRY -- nDepthMul 1/-1 -- groFace: 1=top, 2=front, 3=bottom, 4=back, 5=beg, 6=end -- recCount is for internal use -- Coordinates are always to whole log! function WupAddGro(sWupCode, nDepthMul, bDepthToY, sPieceNum, x1, y1, x2, y2, groDepth, groWidth, groAngle, groFace, groAngleLen, recCount) local nBtlSide, bMirrorY local nAngle, nGroLen, dx, dy, faceHeight, s faceHeight=gnProfHeight if groFace==1 or groFace==3 then faceHeight=gnCurrWidth end if x1<-1 or x1>gnCurrTotLen+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 CNC FILE ANYWAY: x1=%.0f y1=%.0f x2=%.0f y2=%.0f depth=%.0f", x1*1000, y1*1000, x2*1000, y2*1000, groDepth*1000) ) end --toLog( string.format( "GRO1, x1=%.3f y1=%.3f x2=%.3f y2=%.3f groDepth==%.3f groWidth=%.3f groAngle=%.3f groFace=%.3f groAngleLen=%.3f", x1, y1, x2, y2, groDepth, groWidth, groAngle, groFace, groAngleLen ) ) if gnBvnYoffset~=0 then -- Groove coordinates are always to whole log but we have half log raw material. if groFace==2 or groFace==4 or groFace==5 or groFace==6 then y1=y1-gnBvnYoffset y2=y2-gnBvnYoffset end end if false then -- Try birds mouth if WupAddGroAsBird(sPieceNum, x1, y1, x2, y2, groDepth, groWidth, groAngle, groFace, groAngleLen) then return end -- As V-cut at the end if WupAddGroAsVCut(sPieceNum, x1, y1, x2, y2, groDepth, groWidth, groAngle, groFace, groAngleLen) then return end end if groFace>4 then if gnWupWarnSkipMc>0 then AddCncErr( x1, "ERROR: WUP DOES NOT SUPPORT GROOVE AT THE END OF THE PIECE, PLEASE FORCE THE GROOVE TO ONE OF THE SIDES." ) end return end if math.abs(x1-x2)>0.001 or math.abs(y2-y1)<0.001 or math.abs(groAngle)>EPS or math.abs(groAngleLen)>EPS then if gnWupWarnSkipMc>0 then AddCncErr( x1, "ERROR: WUP SUPPORTS ONLY STRAIGHT GROOVES X1=X2 WITHOUT TILT/ROTATION ANGLE, GROOVE SKIPPED" ) end return end if y1>y2 then if gnWupWarnSkipMc>0 then AddCncErr( x1, "ERROR: WUP GROOVE Y1>Y2, GROOVE SKIPPED" ) end return end local ty, tz if bDepthToY then ty=nDepthMul*groDepth tz=y2-y1 else ty=y2-y1 tz=nDepthMul*groDepth end s=string.format("%s %s, %s, %s, %s", sWupCode, WupLen2Str(x1-groWidth), WupLen2Str(groWidth), WupLen2Str(ty), WupLen2Str(tz)) WupAddMc( x1-groWidth, s ) end gsWupChars="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789._-/" function WupToWupStr(s) local res, len, i1, c len=string.len(s) res="" for i1=1,len do c=string.sub(s,i1,i1) if string.find(gsWupChars, c, 1, true) then res=res .. c end end return res end function WupStrRemoveComma(s) local res, len, i1, c len=string.len(s) res="" for i1=1,len do c=string.sub(s,i1,i1) if s~="," then res=res .. c end end return res end -- Returns material index value as string function WupGetMatIndex(sDefault) local res res=ac_objectget("cnc_matindex") if res and res~="" then return WupStrRemoveComma(res) end res=gsWupMatIndex -- Never nil if res=="" and sDefault then res=sDefault end return res end -- isBoard true=current item is a board (nil=not) function WupGetMatName(sId, isBoard) local name sId=WupToWupStr(sId) name=ac_objectget("cnc_matname") if not name or name=="" then if isBoard==nil then isBoard=false end if gbWupMatNameForBoard==isBoard then name=gsWupMatName end end if name and name~="" and name~=" " then sId=WupStrRemoveComma(name) .. " " .. sId end return sId end -- Warns about impossible cuts -- cutLineSide 1-4 the target side -- tblCutLines Cutting lines projected to target side -- coordEnd 0 or plank length -- sBegEnd "beg" or "end" for warnings -- Returns table of points for ending cuts (in order of y-coordinates): -- nil straight cut -- tbl[2] y1<0.001 and y2>width-0.001 -- tbl[3] y1<0.001, y2 between and y3>width-0.001 function WupGetEndPoints(cutLineSide, tblCutLines, coordEnd, sBegEnd) local tblPoints={} local i1, i2, v if #tblCutLines==0 then return nil end i1=1 i2=1 while tblCutLines[i1] do tblPoints[i2]={} tblPoints[i2].x=tblCutLines[i1].x1 tblPoints[i2].y=tblCutLines[i1].y1 i2=i2+1 tblPoints[i2]={} tblPoints[i2].x=tblCutLines[i1].x2 tblPoints[i2].y=tblCutLines[i1].y2 i2=i2+1 i1=i1+1 end -- Sort by y table.sort(tblPoints, function (n1, n2) return n1.y < n2.y end) -- Join similar points into new table local tblJoined={} i1=1 i2=0 while tblPoints[i1] do if i2<1 or math.abs(tblJoined[i2].x - tblPoints[i1].x)>0.001 or math.abs(tblJoined[i2].y - tblPoints[i1].y)>0.001 then i2=i2+1 tblJoined[i2]=tblPoints[i1] end i1=i1+1 end -- Check if possible local faceDy faceDy=gnCurrWidth if cutLineSide==2 or cutLineSide==4 then faceDy=gnProfHeight end if i2==2 then if tblJoined[1].y<0.001 and tblJoined[2].y>faceDy-0.001 then -- Full cut ok elseif tblJoined[1].y<0.001 and tblJoined[2].y0.0009 and math.abs(tblJoined[1].x-coordEnd)<0.001 and tblJoined[2].y>faceDy-0.001 then -- Single cut not whole plank, add first point tblJoined[3]=tblJoined[2] tblJoined[2]=tblJoined[1] tblJoined[1]={} tblJoined[1].x=coordEnd tblJoined[1].y=0 else AddCncErr( coordEnd, string.format("Unsupported %s shape 2 - written as straight", sBegEnd) ) return nil end elseif i2==3 and (tblJoined[1].y>0.001 or tblJoined[3].y0 do x,y,z=WupSidePointToElem(currSide, pointsBeg[i].x, pointsBeg[i].y, tranElem) s=string.format("%s%s,%s, ", s, WupLen2Str(x), WupLen2Str(y)) i=i-1 end for i=1,nEnd do x,y,z=WupSidePointToElem(currSide, pointsEnd[i].x, pointsEnd[i].y, tranElem) s=string.format("%s%s,%s, ", s, WupLen2Str(x), WupLen2Str(y)) end else -- Collect cw for i=1,nBeg do x,y,z=WupSidePointToElem(currSide, pointsBeg[i].x, pointsBeg[i].y, tranElem) s=string.format("%s%s,%s, ", s, WupLen2Str(x), WupLen2Str(y)) end i=nEnd while i>0 do x,y,z=WupSidePointToElem(currSide, pointsEnd[i].x, pointsEnd[i].y, tranElem) s=string.format("%s%s,%s, ", s, WupLen2Str(x), WupLen2Str(y)) i=i-1 end end return s end function WupCncTblToStr() local s, s2 -- From table to text if gTblCnc==nil then return "" end -- 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) s="" for i,v in ipairs(gTblCnc) do s2 = v.text .. ";\r\n" s=s .. s2 end return s end gtblPlankEdges=nil -- If set, the left and right edges are saved here into fields left, right, vecx, ptr. vecx is x-axis' direction vector in master element coordinates gnPlankEdges=0 -- Selects the surface having smaller z-value (closer to the table) -- Returns three values: -- First closer of side1 or side2 -- Second Smaller zoff value -- Third Bigger zoff value function WupFindMinZSide(plankInfo, tranElem, side1, side2) local d, zoff1, zoff2 d,d,zoff1=WupSidePointToElem(plankInfo.tblSides[side1], 0, 0, tranElem) d,d,zoff2=WupSidePointToElem(plankInfo.tblSides[side2], 0, 0, tranElem) if zoff2gnCurrTotLen-0.001 and deptha>0 then -- Handle just full length cuts and forget about the angles if side==sidetop and deptha>topoff then -- Cut from top topoff=deptha elseif side==sidebot and deptha>bottomoff then bottomoff=deptha end end end end height=height-topoff-bottomoff if height<0 then height=0.001 end return bottomoff, topoff, height end -- Calculates y-coordinate for a surface for a plank's machining -- faceFront Same as cutLineSide, the side towards wup camera 1-4 -- Returns: edited yc, boolean is mirrored local function WupGetMcY(faceFront, currFace, plankinfo, y) local mirror=false if faceFront==3 or faceFront==4 then -- Need to mirror, note that watching direction was swapped from element direction at the beginning if currFace<5 then local dy=plankinfo.height if currFace==1 or currFace==3 then dy=plankinfo.width end y=dy-y mirror=true end end return y, mirror end function WupWritePlankInt(unidPlank, plankInfo, tranElem, elemlayer, sPlaPlank, tblDrills) local s, n, i, nOther, x, y, z, nDepth, x1, x2, dx, dy, nWidth, sBin, sPart, sMat, angle local bBegJoint, bEndJoint, nLeftRight local nBtlSide, bMirrorY 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 local cutLineSide local tblCutLinesBeg={} local tblCutLinesEnd={} local by, hz local bEbt local b2ndFraming -- Framing codes except OG/UG (well, let those come anyway) b2ndFraming=false if sPlaPlank and sPlaPlank=="" then b2ndFraming=true sPlaPlank=nil end -- Is it excluded from wup-file bEbt=ac_objectget("iCncFraming") if bEbt and bEbt==-1 then return nil -- Added 7/2023: Skip if cnc framing is minus one end if bEbt~=nil and bEbt==0 then bEbt=true else bEbt=false end if bEbt and sPlaPlank then -- Not core - just discard the element -- let insulation layer planks come --return nil sPlaPlank=nil end gbSkipCncErr=false if sPlaPlank then gbSkipCncErr=true end BvnGetPlankBasicParams(unidPlank) if not sPlaPlank and not gtblPlankEdges then CheckId(gsCurrentLogId, true) end by=gnCurrWidth hz=gnProfHeight local cutoffbottom=0 -- Lengthwise cut may change local cutofftop=0 -- Plank's z-vectors dir in element's coordinates x,y,z=MatTransformVec(plankInfo.vecz.x, plankInfo.vecz.y, plankInfo.vecz.z, tranElem) if z>0.99 or z<-0.99 then --if math.abs(elemData.rotangle)>1 and math.abs(elemData.rotangle-180)>1 and math.abs(elemData.rotangle+180)>1 then -- error("elemdata 0/180") --end cutLineSide=WupFindMinZSide(plankInfo, tranElem, 1, 3) elseif math.abs(z)<0.01 then --if math.abs(elemData.rotangle-90)>1 and math.abs(elemData.rotangle-270)>1 and math.abs(elemData.rotangle+90)>1 then -- error("elemdata 90/270") --end cutLineSide=WupFindMinZSide(plankInfo, tranElem, 2, 4) hz=gnCurrWidth by=gnProfHeight -- Check if (cladding) plank has lengthwise cut cutoffbottom,cutofftop,by=FindLengthwiseCut(by, 1, 3) else AddCncErr( 0, string.format("Unsupported z-vector %f - plank skipped", z) ) return nil end x,y,z=MatTransformVec( plankInfo.vecx.x, plankInfo.vecx.y, plankInfo.vecx.z, tranElem ) if math.abs(z)>0.01 then AddCncErr( 0, string.format("Plank tilt in element is unsupported in wup (z vector=%f)- part skipped", z) ) return nil end gnCncCount=0 gTblCnc=nil gsCncText="" -- Frame plank has all machinings in single table including beginning and ending shapes -- Plank direction in element coordinates local xx, xy, xz, comp, sortstr local yx, yy, yz, ydir local wupx1, wupy1 local currSide, zoff currSide=plankInfo.tblSides[cutLineSide] xx, xy, xz=MatTransformVec( currSide.vecx.x, currSide.vecx.y, currSide.vecx.z, tranElem) yx, yy, yz=MatTransformVec( currSide.vecy.x, currSide.vecy.y, currSide.vecy.z, tranElem) --toLog(string.format("%s\nxx=%f xy=%f xz=%f", ac_objectget("#id"), xx, xy, xz)) --toLog(string.format("yx=%f yy=%f yz=%f", yx, yy, yz)) ydir=ac_geo("linedist", 0, 0, xx, xy, yx, yy) -- Negative=y-axis to left, positive=y-axis to right --toLog(string.format("by=%f cutofftop=%f cutoffbottom=%f ydir=%f", by, cutofftop, cutoffbottom, ydir)) if ydir<0 then wupx1,wupy1,zoff=WupSidePointToElem(currSide, 0, by+cutofftop, tranElem) else wupx1,wupy1,zoff=WupSidePointToElem(currSide, 0, cutoffbottom, tranElem) end -- wupx1, wupy1 is the lower left of local surface, not in target elem's coords --toLog(string.format("wupx1=%f wupy1=%f", wupx1, wupy1)) if gtblPlankEdges then local tblNew tblNew={} tblNew.id=gsCurrentLogId tblNew.ptr=plankInfo.ptr tblNew.right={} tblNew.right.x1, tblNew.right.y1=WupSidePointToElem(currSide, 0, cutoffbottom, tranElem) tblNew.right.x2, tblNew.right.y2=WupSidePointToElem(currSide, plankInfo.len, 0, tranElem) tblNew.left={} tblNew.left.x1, tblNew.left.y1=WupSidePointToElem(currSide, 0, cutoffbottom+by, tranElem) tblNew.left.x2, tblNew.left.y2=WupSidePointToElem(currSide, plankInfo.len, by, tranElem) tblNew.vecx={} tblNew.vecx.x=xx tblNew.vecx.y=xy tblNew.vecx.z=xz gnPlankEdges=gnPlankEdges+1 gtblPlankEdges[gnPlankEdges]=tblNew end -- # Machinings n=ac_objectget("iMc" ,-1) for i=1,n do nType=NormalizeType(ac_objectget("iMc", i, 1)) if nType==EMcFrAngledBeg or nType==EMcFrAngledEnd then local isBeg local nAngleSide, nAngleTop isBeg=(nType==EMcFrAngledBeg) -- Check if full plank cut nAngleTop=ac_objectget("iMc", i, 2) -- In degrees nAngleSide=ac_objectget("iMc", i, 3) -- Bevel (Tilt angle in mc dlg) if ((cutLineSide==1 or cutLineSide==3) and math.abs(nAngleTop-90)>1) or ((cutLineSide==2 or cutLineSide==4) and math.abs(nAngleSide-90)>1) then local tblInfo, tblRes, tblLine tblInfo={} tblInfo.guid = unidPlank tblInfo.mcindex = i tblInfo.cutlineside=cutLineSide tblRes = af_request("mc_getcutinfo", tblInfo) --toLog(string.format("cutLineSide=%d, tblRes.x1=%f tblRes.y1=%f tblRes.x2=%f tblRes.y2=%f", cutLineSide, tblRes.x1, tblRes.y1, tblRes.x2, tblRes.y2)) if math.abs(tblRes.x2-tblRes.x1)+math.abs(tblRes.y2-tblRes.y1)>0.001 then tblLine={} tblLine.x1=tblRes.x1 tblLine.y1=tblRes.y1 tblLine.x2=tblRes.x2 tblLine.y2=tblRes.y2 if isBeg then tblCutLinesBeg[#tblCutLinesBeg+1]=tblLine else tblCutLinesEnd[#tblCutLinesEnd+1]=tblLine end end elseif gnWupWarnSkipMc>0 then AddCncErr( 0, "ERROR: UNSUPPORTED ANGLED CUT SKIPPED, isBeg=%s", tostring(isBeg) ) end elseif 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 -- Decide the wup target surface here local mul, bDepthToY mul=1 bDepthToY=false if groFace==1+((cutLineSide - 1 + 2) % 4) then s="FRY" mul=-1 elseif groFace==cutLineSide then -- Back side s="FRY" else -- Calculate if on the left or right side of the plank local midx1, midy1, midx2, midy2 midx1,midy1=WupSidePointToElem(currSide, 0, cutoffbottom+by*0.5, tranElem) midx2,midy2=WupSidePointToElem(currSide, gnCurrTotLen, cutoffbottom+by*0.5, tranElem) x,y=WupSidePointToElem(plankInfo.tblSides[groFace], 0, 0, tranElem) s="FRZ" bDepthToY=true if ac_geo("linedist", x, y, midx1, midy1, midx2, midy2)<0 then mul=-1 end end if not sPlaPlank then y1=WupGetMcY(cutLineSide, groFace, plankInfo, y1) y2=WupGetMcY(cutLineSide, groFace, plankInfo, y2) if y1>y2 then -- WupAddGro requires x1,y1,x2,y2=x2,y2,x1,y1 end WupAddGro(s, mul, bDepthToY, sPieceNum, x1, y1, x2, y2, groDepth, groWidth, groAngle, groFace, groAngleLen) end elseif nType==EMcFrDrill then if sPlaPlank then -- In element coordinates (cladding piece for example) if tblDrills then WupAddDrillToTbl(tranElem, plankInfo, i, tblDrills) else WupShowErrMsg( string.format("WARNING: DRILL IN %s id=[%s] AND NO DRILLING TABLE - DRILLING SKIPPED", sPlaPlank, ac_objectget("#id")) ) end else -- These are in plank local coordinates local mul 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) mul=1 if groFace==cutLineSide then s="BOZ" elseif groFace==1+((cutLineSide - 1 + 2) % 4) then -- Back side s="BOZ" mul=-1 else -- Calculate if on the left or right side of the plank local midx1, midy1, midx2, midy2 midx1,midy1=WupSidePointToElem(currSide, 0, cutoffbottom+by*0.5, tranElem) midx2,midy2=WupSidePointToElem(currSide, gnCurrTotLen, cutoffbottom+by*0.5, tranElem) x,y=WupSidePointToElem(plankInfo.tblSides[groFace], 0, 0, tranElem) s="BOY" if ac_geo("linedist", x, y, midx1, midy1, midx2, midy2)<0 then mul=-1 end end y1=WupGetMcY(cutLineSide, groFace, plankInfo, y1) -- Inverse always if groFace==1 or groFace==3 then y1=gnCurrWidth-y1 else y1=gnProfHeight-y1 end if groFace>4 then if gnWupWarnSkipMc>0 then AddCncErr( 0, "ERROR: DRILLING AT THE BEG/END OF PIECE SKIPPED" ) end else if groDepth==0 then groDepth=gnCurrWidth if groFace==1 or groFace==3 then groDepth=gnProfHeight end end s=string.format("%s %s, %s, %s, %s", s, WupLen2Str(x1), WupLen2Str(y1), WupLen2Str(groWidth), WupLen2Str(mul*groDepth)) WupAddMc( x1, s ) end end elseif nType==EMcFrMarking and false 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) 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 s=ac_objectget("iMcStr", i, 1) elseif nType==EMcFrNailLine then -- NOP here elseif nType==EMcFrSaw and false then -- Lengthwise sawing if gnWupWarnSkipMc>0 then AddCncErr( 0, "ERROR: UNSUPPORTED ANGLED CUT AT SKIPPED, isBeg=%s", tostring(isBeg) ) end elseif nType~=0 then -- WITH WUP THE PLANK GOES TO SAW FIRST: AddCncErr( ac_objectget("iMc", i, 2), string.format("UNSUPPORTED MACHINING %d SKIPPED", nType) ) end end if gnWupIdSide~=0 and gsCurrentLogId~="" and not sPlaPlank then local sMark s=ac_objectget("iUsageId") if s~="" then sMark=string.format( "%s (%s)", gsCurrentLogId, s) else sMark=string.format( "%s", gsCurrentLogId) end if gnWupIdPos<0 then x=-gnWupIdPos if x>gnCurrTotLen*0.5 then x=gnCurrTotLen*0.5 end elseif gnWupIdPos>0 then x=gnCurrTotLen-gnWupIdPos if x0 or nEnd>0 or (math.abs(xx)>0.001 and math.abs(xy)>0.001) or xy<-0.001 or xx<-0.001 or sPlaPlank then -- If something in either end, add two points to other if missing -- Also if not vertical/horizontal -- Also if vertical from top to bottom -- Also if horizontal from right to left if nBeg==0 then pointsBeg={} pointsBeg[1]={} pointsBeg[1].x=0 pointsBeg[1].y=cutoffbottom pointsBeg[2]={} pointsBeg[2].x=0 pointsBeg[2].y=cutoffbottom+by -- by is total height-cutoffbottom-cutofftop nBeg=2 end if nEnd==0 then pointsEnd={} pointsEnd[1]={} pointsEnd[1].x=gnCurrTotLen pointsEnd[1].y=cutoffbottom pointsEnd[2]={} pointsEnd[2].x=gnCurrTotLen pointsEnd[2].y=cutoffbottom+by nEnd=2 end end local sId sId=gsCurrentLogId if sId=="" then sId="(ID)" end -- Precalculate rectangle coords used in PLx, EBT and others. rdx is nil if not vertical/horizontal local rdx, rdy, rx1, ry1, elemGroup elemGroup=ac_objectget("iElemGroup") if math.abs(xy)>0.999 then rdx=by rdy=gnCurrTotLen elseif math.abs(xx)>0.999 then rdx=gnCurrTotLen rdy=by end if rdx then local x1, y1 -- Find minimum x1,y1 rx1,ry1=WupSidePointToElem(currSide, 0, cutoffbottom, tranElem) x1,y1=WupSidePointToElem(currSide, currSide.dx, cutoffbottom, tranElem) if x1+EPS3 and string.sub(sPlaPlank, 1, 2)=="PL" then writeBt6 = "SL" .. string.sub(sPlaPlank, 3, -1) sPlaPlank=nil end end --toLog(string.format("2: sPlaPlank=%s, nBeg=%d, nEnd=%d", tostring(sPlaPlank), nBeg, nEnd)) if sPlaPlank then -- PLA for plank, just rectangle if not rdx then -- Not supported by wup and must be called from markings return nil end comp=sPlaPlank s=string.format( "%s %s, %s, %s, %s, %s, %s, %s, %s", sPlaPlank, WupLen2Str(rdx), WupLen2Str(rdy), WupLen2Str(hz), WupLen2Str(rx1), WupLen2Str(ry1), WupGetMatIndex("1"), WupGetMatName(sId), WupLen2Str(zoff) ) elseif writeBt6 or (not bEbt and (nBeg>2 or nEnd>2)) then -- Use BT6 if nBeg<3 then pointsBeg[3]=pointsBeg[2] -- Duplicate point end if nEnd<3 then pointsEnd[3]=pointsEnd[2] -- Duplicate point end comp="BT6" if writeBt6 then comp=writeBt6 end s=string.format( "%s %s, %s, %s, %s%s, %s, %s", comp, WupLen2Str(gnCurrTotLen), WupLen2Str(by), WupLen2Str(hz), WupGetBtPoints(currSide, pointsBeg, pointsEnd, tranElem), WupGetMatIndex("1"), WupGetMatName(sId), WupLen2Str(zoff) ) elseif not bEbt and (nBeg==2 and nEnd==2) then comp="BT4" s=string.format( "BT4 %s, %s, %s, %s%s, %s, %s", WupLen2Str(gnCurrTotLen), WupLen2Str(by), WupLen2Str(hz), WupGetBtPoints(currSide, pointsBeg, pointsEnd, tranElem), WupGetMatIndex("1"), WupGetMatName(sId), WupLen2Str(zoff) ) else if not bEbt and (nBeg>0 or nEnd>0) then AddCncErr( 0, "WARNING: Unknown end types - written as straight" ); end -- Select the type based on orientation and usage if math.abs(xy)>math.abs(xx) then -- Vertical comp="QS" else local checkDir, orgguid -- Actually here we need to write lower left in elem coordinates wupx1=rx1 wupy1=ry1 checkDir=true orgguid=plankInfo.orgguid -- If swapped dir if not orgguid then orgguid=plankInfo.guid end if (elemlayer.tblOgGuids and elemlayer.tblOgGuids[orgguid]) or (not elemlayer.tblOgGuids and string.match(elemGroup, "^top_%a*")) then comp="OG" sortstr="002" wasOgUg=true elseif (elemlayer.tblUgGuids and elemlayer.tblUgGuids[orgguid]) or (not elemlayer.tblUgGuids and string.match(elemGroup, "^bottom_%a*")) then comp="UG" sortstr="003" wasOgUg=true end if not wasOgUg then comp="LS" checkDir=false end end if bEbt then -- This is just a rectangle if not rdx then AddCncErr(0, "WARNING: Only vertical or horizontal EBTs are written, part skipped from wup") return nil end comp="EBT" s=string.format( "%s %s, %s, %s, %s, %s, %s, %s, %s", comp, WupLen2Str(rdx), WupLen2Str(rdy), WupLen2Str(hz), WupLen2Str(rx1), WupLen2Str(ry1), WupGetMatIndex("1"), WupGetMatName(sId), WupLen2Str(zoff) ) else s=string.format( "%s %s, %s, %s, %s, %s, %s, %s, %s", comp, WupLen2Str(gnCurrTotLen), WupLen2Str(by), WupLen2Str(hz), WupLen2Str(wupx1), WupLen2Str(wupy1), WupGetMatIndex("1"), WupGetMatName(sId), WupLen2Str(zoff) ) end end -- Top&bottom plates must be in element's direction: Actually never reached since swapped is saved as BT4 if not bEbt and (string.match(elemGroup, "^top_%a*") or string.match(elemGroup, "^bottom_%a*")) and not wasOgUg and CaclDot3D(plankInfo.vecx, g_infoMaster.vecx)<0 then if not gbSkipCncErr then AddCncErr(0, string.format("ERROR: TOP/BOTTOM PLATE DIRECTION INCOMPATIBLE FOR OG/UG (NOW SAVED AS %s) - PLEASE SWAP DIRECTION OF %s", comp, elemGroup) ) end end local tblRes={} local s2 s2=unidPlank if gtblPlankGuid2GroupGuid then s2=gtblPlankGuid2GroupGuid[unidPlank] end s = s .. ";\r\n" -- Weinmann & GranIT specific: Link to btl file if s2 then s = s .. string.format("PROPERTY BTLGUID,\"%s\";\r\n", s2) end s = s .. WupCncTblToStr() tblRes.cnc=s tblRes.guid=unidPlank -- Called with guid tblRes.comp=comp tblRes.x1=wupx1 tblRes.y1=wupy1 tblRes.vecx={} tblRes.vecx.x=xx tblRes.vecx.y=xy tblRes.vecx.z=xz s=ac_objectget("iElemModule") if s==nil then s="" -- Old object end if s~="" and elemlayer.type~="core" then -- Add layer type to the module name s=s .. string.format("-%s", elemlayer.type) end tblRes.module=s tblRes.plankinfo=plankInfo tblRes.plaplank=sPlaPlank return tblRes end -- Adds single drilling to the drilling table -- mcindex Machining index -- infoOrig "plankinfo" for the board/plank -- tblDrills Drills in parent element coordinates, fields x,y,dia,depth,vecz. nil=do not save drills (for planks) function WupAddDrillToTbl(tranMaster, infoOrig, mcindex, tblDrills) local dia, depth, side, x2, y2, z2 x =ac_objectget("iMc", mcindex, 2) y =ac_objectget("iMc", mcindex, 3) dia =ac_objectget("iMc", mcindex, 4) depth =ac_objectget("iMc", mcindex, 5) side =ac_objectget("iMc", mcindex, 6) currSide=infoOrig.tblSides[side] x2,y2,z=MatTransformVec( currSide.vecz.x, currSide.vecz.y, currSide.vecz.z, tranMaster ) if math.abs(z)<0.999 then WupShowErrMsg( string.format("WARNING: DRILL IN [%s] IN BAD SIDE %d - SKIPPED", ac_objectget("#id"), side) ) else -- Transform to the layer (forget z) x, y=WupSidePointToElem(currSide, x, y, tranMaster) local scan, found found=false for scan=1,#tblDrills do if math.abs(tblDrills[scan].x - x)0 and (depth==0.0 or depth>tblDrills[scan].depth) then tblDrills[scan].depth=depth end break end end if not found then -- Collect to table in case there is a drilling between boards local tbl tbl={} tbl.x=x tbl.y=y tbl.dia=dia tbl.depth=depth tbl.vecz=z tblDrills[#tblDrills+1]=tbl end end end -- Gives cnc for drills and nails and saw lines -- infoOrig "plankinfo" for the board/plank -- tblDrills Drills in parent element coordinates, fields x,y,dia,depth,vecz. nil=do not save drills (for planks) -- tblSawCuts Saw cuts in parent element coordinates, fields x1,y1,x2,y2,angledeg,ptr (ref line at right hand side of the cut as in grooves) -- bNailPoints Write as separate points -- tblNails Nailings, nil=create here for current item. Fields: x1, y1, x2, y2, spacing, nailindex (string), point (set only if item is a point: x1=x2, y1=y2. Otherwise nil) -- sNailIndex NR index/nail device -- Returns cnc-text only if using point nailings, also board markings added function WupGetNailDrillCnc(guidCurr, tranMaster, infoOrig, tblDrills, tblSawCuts, bNailPoints, tblNails, sNailIndex, zLayer, tblMarkings) local n, i, nType, hasNail, sCnc, s, x, y, z, currSide, nTblNails, nTblMarkings local x1, y1, x2, y2, groFace, currId nTblNails=#tblNails nTblMarkings=#tblMarkings sCnc="" n=ac_objectget("iMc" ,-1) currId=WupGetPartId() for i=1,n do nType=NormalizeType(ac_objectget("iMc", i, 1)) if nType==EMcFrNailGroup then AddCncErr(0, "ERROR: NAIL GROUP MACHINING NOT SUPPORTED") elseif nType==EMcFrMarking then -- Only board markings if infoOrig.type==4 then local xPosText, yPosText, fontSize, textOff, nParallel, bLine 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 local line = {} line.p0 = {} line.p1 = {} local x1, y1, z1 = WupSidePointToElem(infoOrig.tblSides[groFace], x1, y1, tranMaster, 0) local x2, y2, z2 = WupSidePointToElem(infoOrig.tblSides[groFace], x2, y2, tranMaster, 0) line.p0.x = x1 line.p0.y = y1 line.p0.z = z1 line.p1.x = x2 line.p1.y = y2 line.p1.z = z2 nTblMarkings=nTblMarkings+1 tblMarkings[nTblMarkings]=line end elseif nType==EMcFrNailLine then -- Mc data local x1, y1, x2, y2, z2, cols, rows, anglexRad, angleyRad, spacing, side, len, nails, dx, dy, flags, sNailIndexNow x1 = ac_objectget("iMc", i, 2) y1 = ac_objectget("iMc", i, 3) x2 = ac_objectget("iMc", i, 4) y2 = ac_objectget("iMc", i, 5) spacing = ac_objectget("iMc", i, 6) flags = ac_objectget("iMc", i, 7) side = ac_objectget("iMc", i, 8) sNailIndexNow=sNailIndex dx = ac_objectget("iMc", i, 9) if dx~=0 then sNailIndexNow=string.format("%d", dx) end dx=x2-x1 dy=y2-y1 len=math.sqrt(dx*dx + dy*dy) if spacing<0.001 then spacing=0.001 end if len<0.010 then -- Single point nails=1 else if BitTest(flags, 2) then nails=1+math.floor((len+0.001)/spacing) else nails=2+math.floor((len-0.001)/spacing) spacing=(len - 0.0001)/(nails-1) end end -- Get geometry for current surface currSide=infoOrig.tblSides[side] if bNailPoints then -- Nail points local nail, invLen, xc, yc if len<0.001 then invLen=1 else invLen=1/len end for nail=0,nails-1 do xc=x1+nail*spacing*invLen*dx yc=y1+nail*spacing*invLen*dy -- Transform to the layer (forget z) x,y,z = WupSidePointToElem(currSide, xc, yc, tranMaster) -- NR x1,y1,x2,y2,a,i; s = string.format( "NR %s, %s, %s, %s, 1, %s;\r\n", WupLen2StrNail(x), WupLen2StrNail(y), WupLen2StrNail(x), WupLen2StrNail(y), sNailIndexNow ) sCnc = sCnc .. s end else -- Nail lines, save to table local item x1,y1 = WupSidePointToElem(currSide, x1, y1, tranMaster) x2,y2 = WupSidePointToElem(currSide, x2, y2, tranMaster) item={} item.guid=guidCurr item.x1=x1 item.y1=y1 item.x2=x2 item.y2=y2 item.spacing=spacing item.nailindex=sNailIndexNow item.id=currId if math.abs(x1-x2)0.010 then WupShowErrMsg( string.format("WARNING: SAW CUT IN [%s] TOO WIDE (%.0f mm) - WRITTEN AS SAW CUT ONLY", ac_objectget("#id"), width*1000) ) end tbl={} tbl.x1, tbl.y1 = WupSidePointToElem(currSide, x1, y1, tranMaster) tbl.x2, tbl.y2 = WupSidePointToElem(currSide, x2, y2, tranMaster) tbl.angledeg = ac_objectget("iMc", i, 8) tbl.ptr=infoOrig.ptr --DumpTbl(tbl) tblSawCuts[#tblSawCuts+1]=tbl end end end return sCnc end -- Combines adjacent nailing points into lines -- tblPoints Source data, only nailing points -- dirx, diry Direction of joining unit vector -- Returns table of all combined and point nailings function WupCombineNailPts(tblPoints, dirx, diry) local i1, i2, v1, v2, dx, dy, len, spacing, tblRes, nRes tblRes={} nRes=0 i1=1 while true do v1=tblPoints[i1] if v1==nil then break end -- Scan until no match, i2 will be 1 past last collected point spacing=nil i2=i1 while true do i2=i2+1 v2=tblPoints[i2] if v2==nil or v1.nailindex~=v2.nailindex then break end dx=v2.x1-v1.x1 dy=v2.y1-v1.y1 len=math.sqrt(dx*dx + dy*dy) if len<0.001 then break end dx=dx/len dy=dy/len if math.abs(dx-dirx)>0.001 or math.abs(dy-diry)>0.001 then -- Bad dir break end if spacing==nil then spacing=len -- Second point else if math.abs(len-(i2-i1)*spacing)>0.001 then break -- Spacing does not match end end end -- Save as point first nRes=nRes+1 tblRes[nRes]=v1 if i2>i1+2 then -- Something to join!!! Duplicate not to tamper original item v1=deepcopy(v1) tblRes[nRes]=v1 v1.spacing=spacing v1.x2=tblPoints[i2-1].x1 v1.y2=tblPoints[i2-1].y1 v1.point=nil i1=i2 -- Advance to first incompatible else i1=i1+1 -- Advance to next end end return tblRes end function ToMM10(d) return math.floor(d*10000+0.499)*0.0001 -- For negative math.round(-2.1)=-3 end function WupMarkingStr(line) return string.format("PML;\r\nPP %s,%s,0,122,%s;\r\nPP %s,%s,0,122,%s;\r\n", WupLen2Str(line.p0.x), WupLen2Str(line.p0.y), WupLen2Str(line.p0.z), WupLen2Str(line.p1.x), WupLen2Str(line.p1.y), WupLen2Str(line.p1.z)) end function WupIsPointLessThan(p0, p1) if math.abs(p1.x-p0.x)> 0.001 then return p0.x < p1.x end return p0.y < p1.y end function WupGetDist2D(p0, p1) local diffX=p1.x-p0.x local diffY=p1.y-p0.y return math.sqrt(diffX*diffX + diffY*diffY) end function WupGetLength2D(vec) return math.sqrt(vec.x*vec.x + vec.y*vec.y) end function WupSubtract2D(vecA, vecB) local result={} result.x=vecA.x - vecB.x result.y=vecA.y - vecB.y return result end function WupAdd2D(vecA, vecB) local result={} result.x=vecA.x + vecB.x result.y=vecA.y + vecB.y return result end function WupMultiplty2D(vec, scalar) local result={} result.x=vec.x * scalar result.y=vec.y * scalar return result end function WupGetMinMaxPoint(p0, p1) if WupIsPointLessThan(p0, p1) then return p0, p1 end return p1, p0 end function WupProjectPoint(lineP0, lineP1, point) local vecA=WupSubtract2D(lineP1, lineP0) local vecB=WupSubtract2D(point, lineP0) local lenA=WupGetLength2D(vecA) local length=CaclDot2D(vecA, vecB)/(lenA*lenA) return WupAdd2D(lineP0, WupMultiplty2D(vecA, length)) end function WupFormatPoint(point) return string.format("(%s, %s)", ToMM10(point.x), ToMM10(point.y)) end function WupFormatLine(line) return string.format("%s, %s", WupFormatPoint(line.p0), WupFormatPoint(line.p1)) end -- Indicates if source and target lines are colinear within some error threshold function WupAreLinesCollinear(source, target, maxVerticalError) local sourceMin, sourceMax=WupGetMinMaxPoint(source.p0, source.p1) local projMin=WupProjectPoint(sourceMin, sourceMax, target.p0) local projMax=WupProjectPoint(sourceMin, sourceMax, target.p1) local projMin, projMax=WupGetMinMaxPoint(projMin, projMax) local verticalErrorA=WupGetDist2D(projMin, target.p0) local verticalErrorB=WupGetDist2D(projMax, target.p1) local horizontalDiff=WupGetDist2D(projMin, sourceMax) if WupGetLength2D(WupSubtract2D(projMin, sourceMin)) < WupGetLength2D(WupSubtract2D(sourceMax, sourceMin)) then horizontalDiff = horizontalDiff * -1 end return verticalErrorA < maxVerticalError and verticalErrorB < maxVerticalError, horizontalDiff end function WupCombineDistanceOK(horizontalDiff) local combineOverlappingMarkingsOnly = gnWupCombineMarkingMaxDist < 0.0 if combineOverlappingMarkingsOnly then return horizontalDiff < 0.0 and gnWupCombineMarkingMaxDist <= horizontalDiff end return horizontalDiff <= gnWupCombineMarkingMaxDist end -- Creates cnc text with combined marking lines function WupCombineMarkings(markings) if markings == nil then return "" end local cnc="" if gnWupCombineMarkingMaxDist <= -0.999 then for i = 1, #markings do cnc = cnc .. WupMarkingStr(markings[i]) end return cnc end table.sort(markings, function (lineA, lineB) local lineAMin, lineAMax = WupGetMinMaxPoint(lineA.p0, lineA.p1); local lineBMin, lineBMax = WupGetMinMaxPoint(lineB.p0, lineB.p1); return WupIsPointLessThan(lineAMin, lineBMin); end) markingsToProcess = markings while #markingsToProcess > 0 do local combined=markingsToProcess[1] local nextMarkingsToProcess={} for i = 2, #markingsToProcess do local target=markingsToProcess[i] local areCollinear, horizontalDiff = WupAreLinesCollinear(combined, target, 0.001) if areCollinear then if WupCombineDistanceOK(horizontalDiff) then local nextMin, nextMax=WupGetMinMaxPoint(target.p0, target.p1) if WupIsPointLessThan(combined.p0, combined.p1) then combined.p1=nextMax else combined.p0=nextMax end else cnc = cnc .. WupMarkingStr(combined) combined = target end else table.insert(nextMarkingsToProcess, target) end end cnc = cnc .. WupMarkingStr(combined) markingsToProcess = nextMarkingsToProcess end return cnc end -- Returns the distance the nail gun travels function WupCalcNailingLen(tblNails) local x, y, res, i, t, dx, dy if #tblNails<1 then return 0 end res=0 i=1 x=tblNails[1].x1 y=tblNails[1].y1 while true do t=tblNails[i] if not t then break end i=i+1 -- Dist from previous to current dx = x - t.x1 dy = y - t.y1 res = res + math.sqrt(dx*dx + dy*dy) x = t.x1 y = t.y1 if not t.point then -- It is a line dx = x - t.x2 dy = y - t.y2 res = res + math.sqrt(dx*dx + dy*dy) x = t.x2 y = t.y2 end end return res end -- Combines nailings and creates cnc-text -- tblNails Collected nailings with fields: x1, y1, x2, y2, spacing, nailindex (string), point (set only if item is a point: x1=x2, y1=y2. Otherwise nil). -- bJoinPoints true=try joining all the point type nailings function WupNailTblToStr(tblNails, bJoinPoints) local i, v, n, tblNailsNow local lineEps lineEps=0.001 tblNailsNow=tblNails if bJoinPoints then -- Collect all point nailings local tblPointsVert, tblPointsHor tblPointsVert={} tblPointsHor={} n=0 for i,v in ipairs(tblNails) do if v.point then n=n+1 tblPointsVert[n]=v tblPointsHor[n]=v end end if n>2 then local tblResVert, tblResHor -- Try combining with vertical lines table.sort(tblPointsVert, function (n1, n2) if math.abs(n1.x1 - n2.x1)>lineEps then return n1.x1lineEps then return n1.y1#tblResHor then tblResVert=tblResHor end if #tblResVertlineEps then return n1.x1lineEps then return n1.y1dy then -- Horizontal planks fnSort = function(p1, p2) if math.abs(p1.info.elemdata.y1 - p2.info.elemdata.y1)>EPS then return p1.info.elemdata.y1 < p2.info.elemdata.y1 end return p1.info.elemdata.x1 < p2.info.elemdata.x1 end else -- Vertical planks fnSort = function(p1, p2) if math.abs(p1.info.elemdata.x1 - p2.info.elemdata.x1)>EPS then return p1.info.elemdata.x1 < p2.info.elemdata.x1 end return p1.info.elemdata.y1 < p2.info.elemdata.y1 end end -- Sort planks tblSorted={} for k,v in pairs(tblGuid2Info) do v.guid=k tblSorted[#tblSorted+1]=v end table.sort(tblSorted, fnSort) -- And save plank order to tblGuid2Info for k,v in ipairs(tblSorted) do tblGuid2Info[v.guid].index = k end -- Then the nailings table.sort(tblNailsNow, function(n1, n2) local p1, p2, dummy, d1, d2 p1 = tblGuid2Info[n1.guid] p2 = tblGuid2Info[n2.guid] if p1.index~=p2.index then return p1.index < p2.index end -- In the same plank, then in order of the plank's dir dummy, d1 = ac_geo ("linedist", p1.info.elemdata.x1, p1.info.elemdata.y1, p1.info.elemdata.x2, p1.info.elemdata.y2, n1.x1, n1.y1) dummy, d2 = ac_geo ("linedist", p1.info.elemdata.x1, p1.info.elemdata.y1, p1.info.elemdata.x2, p1.info.elemdata.y2, n2.x1, n2.y1) return d1 < d2 end) end end -- Write the nailings tableTot={} for i,v in ipairs(tblNailsNow) do -- NR x1,y1,x2,y2,a,i; -- HACK 9/2019: Weinmann takes given coordinates and spacing and starts spacing from begin point until the end. We can use single decimal for 0,1 mm accuracy -- ArchiFrame already has adjusted the spacing to 0,1 mm but still here can come rounding problems because begin and end points roundings -- So adjust the end point to be beyond spacing value local x1, y1, x2, y2, dx, dy, len, spacing -- Round coordinates to 1/10 mm x1=ToMM10(v.x1) y1=ToMM10(v.y1) x2=ToMM10(v.x2) y2=ToMM10(v.y2) spacing=ToMM10(v.spacing) --ac_environment("tolog", string.format("x1=%f y1=%f x2=%f y2=%f v.x1=%f v.y1=%f v.x2=%f v.y2=%f spacing=%f",x1, y1, x2, y2, v.x1, v.y1, v.x2, v.y2, spacing)) dx=x2-x1 dy=y2-y1 len=math.sqrt(dx*dx + dy*dy) if len>0.001 then local intpart, decpart local maxAdjust=0.005 -- How much each end may be adjusted automatically intpart,decpart=math.modf(len/spacing) if decpart*spacingy2 then y2=elemdata.by2 end else if elemdata.by1othery2 then othery2=elemdata.by2 end end end end --toLog(string.format("y1=%f othery1=%f y2=%f othery2=%f\n", y1, othery1, y2, othery2)) -- Then mark all lowest and highest pieces if y1 and y1othery2-0.001 then elemlayer.tblOgGuids={} for i,v in ipairs(tblHor) do if v.elemdata.by2+0.001>y2 then --toLog(string.format("OG %s\n", v.guid)) elemlayer.tblOgGuids[v.guid]=true end end end end -- tranMaster Translation matrix from global coordinates to the element's coordinate system -- elemlayer Layer from af_request("elem_openparent") -- tblPlanks 1-based table of planks to handle for this element sorted by ID -- tblDrills Drills in parent element coordinates -- tblNails Nailings in parent element coordinates -- Returns 1-based index of planks containing fields as WupWritePlank. Also field cncnail if point nailings function WupDoCoreLayer(tranMaster, elemlayer, tblPlanks, tblDrills, tblNails, zLayer, tblMarkings) local tblRes={} local nRes=0 local i, v, plankInfo local sNail sNail="20" if gsWupNailGun then sNail=gsWupNailGun end WupMakeOgUgTables(tranMaster, elemlayer, tblPlanks) af_request("elem_openlayer", elemlayer.guid) for i,v in ipairs(tblPlanks) do ac_objectopen(v) plankInfo=af_request("plankinfo", nil, g_infoMasterZCam) nRes=nRes+1 tblRes[nRes]=WupWritePlank(v, plankInfo, tranMaster, elemlayer, nil, tblDrills) if tblRes[nRes] then tblRes[nRes].cncnail=WupGetNailDrillCnc(v, tranMaster, plankInfo, nil, nil, false, tblNails, sNail, zLayer, tblMarkings) -- tblDrills nil kapuloille else nRes=nRes-1 end ac_objectclose() end af_request("elem_closelayer") return tblRes end -- Handles planks on paneling layer. Params as in WupDoCoreLayer -- tblNails Nailings in parent element coordinates -- sPlaPlank For paneling/studding "PLI1-10" / "PLA1-10", for secondary framing "" function WupDoExtraPlankLayer(tranMaster, elemlayer, tblPlanks, tblDrills, tblNails, sPlaPlank, zLayer, tblMarkings) local tblRes={} local nRes=0 local i, v, plankInfo, s, currItem local sNail sNail="20" if gsWupNailGun then sNail=gsWupNailGun end af_request("elem_openlayer", elemlayer.guid) for i,v in ipairs(tblPlanks) do ac_objectopen(v) plankInfo=af_request("plankinfo", nil, g_infoMasterZCam) currItem=WupWritePlank(v, plankInfo, tranMaster, elemlayer, sPlaPlank, tblDrills) if currItem then nRes=nRes+1 tblRes[nRes]=currItem s=WupGetNailDrillCnc(v, tranMaster, plankInfo, nil, nil, false, tblNails, sNail, zLayer, tblMarkings) -- tblDrills nil kapuloille currItem.cnc=currItem.cnc .. s -- Just point nailings, others are collected if currItem.module~="" and gtblWupModulItems then -- Belongs to a module which can be just in the framing layer - save to be processed in it and skip local exist, imod, vmod -- Check if exists already since this may be called multiple times (for markings) exist=false for imod,vmod in ipairs(gtblWupModulItems) do if vmod.plankinfo.ptr==currItem.plankinfo.ptr then exist=true break end end if not exist then gtblWupModulItems[#gtblWupModulItems+1]=currItem end tblRes[nRes]=nil nRes=nRes-1 end end ac_objectclose() end af_request("elem_closelayer") return tblRes end function WupIsCorner(tblPoly, x, y) if math.abs(x-tblPoly.x1)0.99 or z<-0.99 then cutLineSide=WupFindMinZSide(plankInfo, tranElem, 1, 3) else cutLineSide=WupFindMinZSide(plankInfo, tranElem, 2, 4) end -- Frame plank has all machinings in single table including beginning and ending shapes -- Plank direction in element coordinates local wupx1, wupy1 local currSide, zoff currSide=plankInfo.tblSides[cutLineSide] wupx1,wupy1,zoff=WupSidePointToElem(currSide, 0, 0, tranElem) return zoff end -- Gives string for the raw boards (PLAx) -- elemlayer Layer from af_request("elem_openparent") -- tblBoards 1-based table of boards to handle for this element, adds fields plankinfo, thickness, elemx1, elemy1, elemx2, elemy2 into each board -- tranMaster Translation matrix from global coordinates to the element's coordinate system -- sPlaBoard The wup-command "PLI1-10" / "PLA1-10" -- matRef The wup index (material reference) -- tblDrills Drills in parent element coordinates -- tblSawCuts Saw cuts in parent element coordinates -- tblNails Nailings in parent element coordinates -- nPsgSign +-1, sign to get into core layer (defines sign of sawing depth) -- Returns cnc-string, cnc for nails (only point nailings) function WupDoBoardsLayer(tranMaster, elemlayer, tblBoards, sPlaBoard, matRef, zLayer, tblDrills, tblSawCuts, tblNails, nPsgSign, tblMarkings) local i, v, sRes, s, sCncNails, sBoardLayer, polyCnc, zBoard local sNail sNail="10" if gsWupNailGun then sNail=gsWupNailGun end -- Calculate position in elem coordinates af_request("elem_openlayer", elemlayer.guid) for i,v in ipairs(tblBoards) do v.poly=af_request("getpoly", nil, v.guid) ac_objectopen(v.guid) v.plankinfo=af_request("plankinfo") v.thickness=ac_objectget("iThickness") -- 6/2024: Perhaps should use: zBoard = WupGetBoardZ(v.plankinfo, tranMaster) v.cncnail=WupGetNailDrillCnc(v.guid, tranMaster, v.plankinfo, tblDrills, tblSawCuts, false, tblNails, sNail, zLayer, tblMarkings) ac_objectclose() -- Calculate xy-coordinates in parent (polygon is on board front face) v.elemx1,v.elemy1=WupSidePointToElem(v.plankinfo.tblSides[2], v.poly.x1, v.poly.y1, tranMaster) v.elemx2,v.elemy2=WupSidePointToElem(v.plankinfo.tblSides[2], v.poly.x2, v.poly.y2, tranMaster) --ac_environment("tolog", string.format("v.elemx1=%f, v.elemy1=%f", v.elemx1, v.elemy1)) end af_request("elem_closelayer") table.sort(tblBoards, function (n1, n2) if math.abs(n1.elemx1-n2.elemx1)>0.001 then return n1.elemx1first do polyPoints[i].openingtype=polyPoints[i-1].openingtype i=i-1 end polyPoints[first].openingtype=temp -- Swap direction points=last-first+1 half=math.floor(points/2) for i=0,half-1 do temp=polyPoints[first+i] polyPoints[first+i]=polyPoints[last-i] polyPoints[last-i]=temp end -- Move end statuses from old last point to old first point polyPoints[last].isendcontour=polyPoints[first].isendcontour -- true polyPoints[last].isendpolygon=polyPoints[first].isendpolygon polyPoints[first].isendcontour=nil polyPoints[first].isendpolygon=nil if not swapHoles or polyPoints[last].isendpolygon then break end first=last+1 end end -- Checks if line dir from pt1 to pt2 is different from vecNoCuts function WupIsNoDir(vecNoCuts, pt1, pt2) local dx,dy,len dx=pt2.x-pt1.x dy=pt2.y-pt1.y len=math.sqrt(dx*dx + dy*dy) if len<0.001 then return false end dx=dx/len dy=dy/len if math.abs(dx*vecNoCuts.x+dy*vecNoCuts.y)>0.99 then return true end return false end -- Returns: nil=no special, string=cnc-text to add function WupCheckSpecCut(tranMaster, currSide, tblEdge, bHole, depth, z, sType, nWupOpeningBotTop) local n, s, c1, c2 n=tblEdge.c1.openingtype if nWupOpeningBotTop==1 and n then c1=tblEdge.c1 c2=tblEdge.c2 if c1.x>c2.x then c1,c2=c2,c1 end if (n==11 or n==21) and math.abs(c1.y - c2.y)<0.001 then -- Door or window top, add two cuts: -- Lower at the opening top-46 mm no angle, extend 49 mm left and right -- Upper at window top and cut with 15 deg angle -- Lower s="" s=s .. string.format( "%s;\r\n", sType ) s=s .. string.format( "PP %s,%s,%s,121,0,%s;\r\n", WupLen2Str(c1.x-0.047), WupLen2Str(c1.y-0.050), WupLen2Str(depth), WupLen2Str(z) ) -- 121: 100=Tool on left, 20=undercut, 1=saw blade s=s .. string.format( "PP %s,%s,%s,121,0,%s;\r\n", WupLen2Str(c2.x+0.047), WupLen2Str(c1.y-0.050), WupLen2Str(depth), WupLen2Str(z) ) -- Upper s=s .. string.format( "%s;\r\n", sType ) s=s .. string.format( "PP %s,%s,%s,221,15, %s;\r\n", WupLen2Str(c1.x-0.049), WupLen2Str(c1.y-0.007), WupLen2Str(depth), WupLen2Str(z) ) -- 221: 200=Tool on right, 20=undercut, 1=saw blade s=s .. string.format( "PP %s,%s,%s,221,15, %s;\r\n", WupLen2Str(c2.x+0.049), WupLen2Str(c1.y-0.007), WupLen2Str(depth), WupLen2Str(z) ) return s end if n==13 and math.abs(c1.y - c2.y)<0.001 then -- Window bottom, add two cuts: -- Lower at opening bottom no angle, extend 49 mm left and right -- Upper +46 mm with 15 deg angle, extend 49 mm left and right -- Upper s="" s=s .. string.format( "%s;\r\n", sType ) s=s .. string.format( "PP %s,%s,%s,221,15,%s;\r\n", WupLen2Str(c1.x-0.049), WupLen2Str(c1.y+0.046-0.003), WupLen2Str(depth), WupLen2Str(z) ) -- 221: 200=Tool on right, 20=undercut, 1=saw blade s=s .. string.format( "PP %s,%s,%s,221,15,%s;\r\n", WupLen2Str(c2.x+0.049), WupLen2Str(c1.y+0.046-0.003), WupLen2Str(depth), WupLen2Str(z) ) -- Lower s=s .. string.format( "%s;\r\n", sType ) s=s .. string.format( "PP %s,%s,%s,121,0,%s;\r\n", WupLen2Str(c1.x-0.049), WupLen2Str(c1.y), WupLen2Str(depth), WupLen2Str(z) ) -- 121: 100=Tool on left, 20=undercut, 1=saw blade s=s .. string.format( "PP %s,%s,%s,121,0,%s;\r\n", WupLen2Str(c2.x+0.049), WupLen2Str(c1.y), WupLen2Str(depth), WupLen2Str(z) ) return s end end return nil end -- nPsfType==nil: PP 1252,1364,-13,221,0,0; -- If not nil: PP 1252,1364,-13,0,0,0; function WupGetPP( nPsfType, x, y, depth, sControl, z ) if nPsfType then return string.format( "PP %s,%s,%s,%d,0,%s;\r\n", WupLen2Str(x), WupLen2Str(y), WupLen2Str(depth), nPsfType, WupLen2Str(z) ) else return string.format( "PP %s,%s,%s,%s,0,%s;\r\n", WupLen2Str(x), WupLen2Str(y), WupLen2Str(depth), sControl, WupLen2Str(z) ) end end -- Shortens begin and end unless skipped -- Returns: true=ok, false=skip whole line -- Then x1, y1, x2, y2 function WupShortenLine(x1, y1, x2, y2, doBeg, doEnd, dShorten) local dx, dy, len, n n=0 if doBeg then n=n+1 end if doEnd then n=n+1 end --ac_environment("tolog", string.format("pre: x1=%f, y1=%f, x2=%f, y2=%f, doBeg=%s, doEnd=%s", x1, y1, x2, y2, tostring(doBeg), tostring(doEnd))) dx=x2-x1 dy=y2-y1 len=math.sqrt(dx*dx + dy*dy) if len<0.001 or len0.001 then -- p15=2 -- Over at begin -- elseif prev.overbegin>0.001 and v.overendin<=0.001 then -- p15=3 -- Over at end -- else -- p15=1 -- No over -- end tblLine={} tblLine.cutfromobj=prev.cutfromobj tblLine.c1=prev tblLine.c2=v tblLine.overcut1=(prev.overbegin<0.010) -- tooldia=0.050 tblLine.overcut2=(v.overendin<0.010) nLines=nLines+1 tblLines[nLines]=tblLine end prev=v if v.isendcontour then -- Duplicate start point, coordinates already changed endv=polyPoints[begInd] tblLine={} tblLine.cutfromobj=v.cutfromobj tblLine.c1=v tblLine.c2=endv tblLine.overcut1=(v.overbegin<0.010) -- tooldia=0.050 tblLine.overcut2=(endv.overendin<0.010) nLines=nLines+1 tblLines[nLines]=tblLine if v.isendpolygon then break end WupShowErrMsg( "WARNING: POLYGON WITH HOLES WRITTEN - CASE NOT TESTED ***"); begInd=i+1 prev=nil end end -- Process the lines local nextprev sCnc="" i=1 prev=nil -- Set if c1 written at last round if nPsfType==nil then if sType=="PSF" then nPsfType=0 -- true in lua anyway end end local sControl if nPsfType then -- This code writes as polygon - better to write as individual lines except for psf/no nailings area -- 221: 200=Tool on the right, [20=undercut, 10=overcut], 1=Cylindrical trimmer (Note 7.6.2021: sControl not used if nPsfType != nil) if bHole then sControl="221" else sControl="211" -- 211: 200=Tool on the right, , 1=Cylindrical trimmer end while i<=nLines do v=tblLines[i] if v.cutfromobj~=nil then WupShowErrMsg( "ERROR: WupGetLayerPoly/v.cutfromobj~=nil. CONTACT SUPPORT" ) end nextprev=v if vecNoCuts and WupIsNoDir(vecNoCuts, v.c1, v.c2) then -- Flush last line if any points written if prev then --s=string.format( "PP %s,%s,%s,%s,0,%s;\r\n", WupLen2Str(prev.c2.x), WupLen2Str(prev.c2.y), WupLen2Str(depth), sControl, WupLen2Str(z) ) s=WupGetPP(nPsfType, prev.c2.x, prev.c2.y, depth, sControl, z) sCnc=sCnc .. s end nextprev=nil else s=WupCheckSpecCut(tranMaster, currSide, v, bHole, depth, z, sType, nWupOpeningBotTop) if s then -- Flush if prev then --sCnc=sCnc .. string.format( "PP %s,%s,%s,%s,0,%s;\r\n", WupLen2Str(prev.c2.x), WupLen2Str(prev.c2.y), WupLen2Str(depth), sControl, WupLen2Str(z) ) sCnc=sCnc .. WupGetPP(nPsfType, prev.c2.x, prev.c2.y, depth, sControl, z) end nextprev=nil sCnc=sCnc .. s else -- Write starting point if not prev then sCnc=sCnc .. string.format( "%s;\r\n", sType ) end --s=string.format( "PP %s,%s,%s,%s,0,%s;\r\n", WupLen2Str(v.c1.x), WupLen2Str(v.c1.y), WupLen2Str(depth), sControl, WupLen2Str(z) ) s=WupGetPP(nPsfType, v.c1.x, v.c1.y, depth, sControl, z) sCnc=sCnc .. s if v.c1.isendcontour then -- Write also c2 --s=string.format( "PP %s,%s,%s,%s,0,%s;\r\n", WupLen2Str(v.c2.x), WupLen2Str(v.c2.y), WupLen2Str(depth), sControl, WupLen2Str(z) ) s=WupGetPP(nPsfType, v.c2.x, v.c2.y, depth, sControl, z) sCnc=sCnc .. s nextprev=nil end end end prev=nextprev i=i+1 end else -- Write as separate lines local nControl, controlNow, overCut, underCut, write -- 221: 200=Tool on the right, [20=undercut, 10=overcut], 1=Cylindrical trimmer overCut=10 underCut=20 if bHole then -- This is other way round - hole polygons processed overCut=20 underCut=10 end nControl=201 while i<=nLines do v=tblLines[i] write=false if not vecNoCuts or not WupIsNoDir(vecNoCuts, v.c1, v.c2) then write=true end if v.cutfromobj~=nil then --ac_environment("tolog", string.format("v.cutfromobj=%d", v.cutfromobj)) -- This overrides previous decision write=false if v.cutfromobj==1 then write=true end end if write then s=nil if v.cutfromobj==nil then s=WupCheckSpecCut(tranMaster, currSide, v, bHole, depth, z, sType, nWupOpeningBotTop) end if s then sCnc=sCnc .. s else -- Write whole line local x1,y1,x2,y2, lineOk, shortenBeg, shortenEnd x1=v.c1.x y1=v.c1.y x2=v.c2.x y2=v.c2.y lineOk=true shortenBeg=(not v.overcut1) shortenEnd=(not v.overcut2) if bHole then shortenBeg=not shortenBeg shortenEnd=not shortenEnd end if nShortenUnderCut and math.abs(nShortenUnderCut)>EPS and (shortenBeg or shortenEnd) then lineOk,x1,y1,x2,y2=WupShortenLine(x1, y1, x2, y2, shortenBeg, shortenEnd, nShortenUnderCut) end if lineOk then sCnc=sCnc .. string.format( "%s;\r\n", sType ) -- BEG controlNow=nControl+underCut -- Undercut if v.overcut1 then controlNow=nControl+overCut -- overcut end s=WupGetPP(nPsfType, x1, y1, depth, string.format("%d", controlNow), z) sCnc=sCnc .. s -- END controlNow=nControl+underCut -- Undercut if v.overcut2 then controlNow=nControl+overCut -- overcut end s=WupGetPP(nPsfType, x2, y2, depth, string.format("%d", controlNow), z) sCnc=sCnc .. s end end end i=i+1 end end return sCnc end gtblWupCnc=nil -- If set, will be collected into the buf. Otherwise to io.write gnWupCnc=0 -- Index to previous if collecting gbWupWriteNil=false -- Set to true not write anything function WupWriteCnc( s ) if s==nil then error("WupWriteCnc(nil)") end if s=="" then return end if gtblWupCnc then gnWupCnc=gnWupCnc+1 gtblWupCnc[gnWupCnc]=s elseif not gbWupWriteNil then io.write(s) end end function WupGetSawCutCnc(sType, vsaw, depth, zLayer) local s s="" s=s .. string.format( "%s;\r\n", sType ) s=s .. string.format( "PP %s,%s,%s,121,%s, %s;\r\n", WupLen2Str(vsaw.x1), WupLen2Str(vsaw.y1), WupLen2Str(depth), WupInt2Str(vsaw.angledeg), WupLen2Str(zLayer) ) s=s .. string.format( "PP %s,%s,%s,121,%s, %s;\r\n", WupLen2Str(vsaw.x2), WupLen2Str(vsaw.y2), WupLen2Str(depth), WupInt2Str(vsaw.angledeg), WupLen2Str(zLayer) ) -- 121: 100=Tool on left (200=right), 20=undercut, 1=saw blade return s end function WupGetPolyMid(points) local i, v, x1, y1, x2, y2 x1=1e100 y1=x1 x2=-x1 y2=-x1 for i,v in ipairs(points) do if v.xx2 then x2=v.x end if v.y>y2 then y2=v.y end end return (x1+x2)*0.5, (y1+y2)*0.5 end -- tranMaster Transformation to the core layer -- tblPlanks All planks -- kplank Index/key to current plank of vplank -- panelParam nil for core, PLx for others -- Returnst three values: -- cnc text for the whole module transformed to be relative to modules left bottom -- nail cnc not transformed (as it is currently in the planks), may be nil -- next key to tblPlanks function WupHandleModule(tranMaster, tblPlanks, kplank, elemlayer, panelParam) -- Find last item and the bounding box local kend, v, module, x1, x2, y1, y2, z1, z2, cnc, cncnail, tblRes -- Find bounding box & make nailings cnc v=tblPlanks[kplank] module=v.module x1=v.plankinfo.elemdata.bx1 x2=v.plankinfo.elemdata.bx2 y1=v.plankinfo.elemdata.by1 y2=v.plankinfo.elemdata.by2 z1=v.plankinfo.elemdata.bz1 z2=v.plankinfo.elemdata.bz2 cncnail=nil kend=kplank -- Process current also while true do v=tblPlanks[kend] if v==nil or v.module~=module then break end kend=kend+1 if x1>v.plankinfo.elemdata.bx1 then x1=v.plankinfo.elemdata.bx1 end if x2v.plankinfo.elemdata.by1 then y1=v.plankinfo.elemdata.by1 end if y2v.plankinfo.elemdata.bz1 then z1=v.plankinfo.elemdata.bz1 end if z2z2 then -- Should be always z1,z2=z2,z1 end cnc=string.format("MODUL %s, %s, %s, %s, %s, %s, %s;\r\n", WupLen2Str(x2-x1), WupLen2Str(y2-y1), WupLen2Str(z2-z1), WupLen2Str(x1), WupLen2Str(y1), WupStrToWup(module), WupLen2Str(z1)) local tranModul tranModul=deepcopy(tranMaster) tranModul[4]=tranModul[4] - x1 * g_infoMaster.vecx.x - y1 * g_infoMaster.vecz.x - z1 * g_infoMaster.vecy.x tranModul[8]=tranModul[8] - x1 * g_infoMaster.vecx.y - y1 * g_infoMaster.vecz.y - z1 * g_infoMaster.vecy.y tranModul[12]=tranModul[12] - x1 * g_infoMaster.vecx.z - y1 * g_infoMaster.vecz.z - z1 * g_infoMaster.vecy.z while kplankEPS and (v.overbeg==0 or v.overend==0) then local shortenBeg, shortenEnd shortenBeg=false shortenEnd=false if v.overbeg==0 then shortenBeg=true end if v.overend==0 then shortenEnd=true end lineOk,x1,y1,x2,y2=WupShortenLine(x1, y1, x2, y2, shortenBeg, shortenEnd, nShortenUnderCut) end if lineOk then -- 200=Tool on the right, [20=undercut, 10=overcut], 1=Cylindrical trimmer local nControl, nControlBeg, nControlEnd, angle, swapCamera, side, depthNow, sPsgPaf, objTool -- TODO: check if reversed dir, handle bladeside, angle swapCamera=false nControl=1 -- Standard saw blade -- Check the tool number from the saw cut object sPsgPaf="PSG" ac_objectopen(v.elemguid) objTool=ac_objectget("iToolType") if objTool and objTool==2 then -- It is a router sPsgPaf="PAF" end ac_objectclose() side=v.bladeside if swapCamera then side=-side end if side<0 then nControl=nControl+100 elseif side>0 then nControl=nControl+200 end nControlBeg=nControl if v.overbeg>0 then nControlBeg=nControlBeg+10 else nControlBeg=nControlBeg+20 end nControlEnd=nControl if v.overend>0 then nControlEnd=nControlEnd+10 else nControlEnd=nControlEnd+20 end depthNow=depth if math.abs(v.depth)>0.0001 then local sgn=GetSgn(depth) depthNow=v.depth+extraDepth if sgn~=GetSgn(depthNow) and sgn~=0 then depthNow=-depthNow end end angle=v.angledeg if swapCamera then angle=-angle end s=string.format("%s 2;\r\nPP %s,%s,%s,%d,%0.1f,%s;\r\n", sPsgPaf, WupLen2Str(x1), WupLen2Str(y1), WupLen2Str(depthNow), nControlBeg, angle, WupLen2Str(zSaw)) s=s .. string.format("PP %s,%s,%s,%d,%0.1f,%s;\r\n", WupLen2Str(x2), WupLen2Str(y2), WupLen2Str(depthNow), nControlEnd, angle, WupLen2Str(zSaw)) --ac_environment("tolog", s) if v.ishole==1 then cncHoles=cncHoles .. s else cncMain=cncMain .. s end end end if contourFirst and cncMain~="" then WupWriteCnc(cncMain) cncMain="" end -- Write holes WupWriteCnc(cncHoles) return cncMain end -- If guid is nil, the board is already opened with ac_objectopen(). Otherwise the part is opened here -- Returns: true=not a board or board going to cnc, false=was a board having iCnc==0 function WupExportPart(guid) local info, cnc, res if guid then ac_objectopen(guid) end res = true info = af_request("plankinfo") if info and info.type == 4 then cnc = ac_objectget("iCnc") if cnc and cnc == 0 then res = false end end if guid then ac_objectclose() end return res end -- Writes single layer (either planks or boards) -- sPlaPlank PLA/I1-10 for planks -- sPlaBoard PLA/I1-10 for boards -- nPsgSign +-1, sign to get into core layer (defines sign of sawing depth) -- Returns: 0=no PLx-code used, 1=PLx code used function WupWriteElemLayer(tranMaster, vlayer, sPlaPlank, sPlaBoard, mapElemToPlanks, bCore, nPsgSign) local infoOwner, quant, tblDrills, tblNails, sCncNails, s, plankInfo, zLayer, vecNoCuts, panelParam local retVal retVal=0 infoOwner=af_request("plankinfo", vlayer.guid) quant=af_request("elem_quantities", vlayer.guid, 0).tblelems[1] -- For this single layer only gsWupNailGun=nil gsWupMatIndex="" gsWupIsFraming="" gsWupMatName="" gbWupMatNameForBoard=false if quant.layertypenum==1 then gbWupMatNameForBoard=true end if quant.xmlutf8 then -- At the end of the definition there is -- Quick&dirty: no real parsing gsWupNailGun=string.match(quant.xmlutf8, " cnc_nailgun=\"(.-)\"") if gsWupNailGun and gsWupNailGun=="" then -- Not necessarily used: ac_environment("tolog", string.format("WUP-WARNING: cnc_nailgun for layer %s is empty - using default nail index", vlayer.type)) gsWupNailGun=nil end s=string.match(quant.xmlutf8, " cnc_matindex=\"(.-)\"") if s then gsWupMatIndex=s end s=string.match(quant.xmlutf8, " cnc_matname=\"(.-)\"") if s then gsWupMatName=s end s=string.match(quant.xmlutf8, " cnc_isframing=\"(.-)\"") if s then gsWupIsFraming=s end end plankInfo=af_request("plankinfo", vlayer.guid) s,zLayer=WupFindMinZSide(plankInfo, tranMaster, 2, 4) tblDrills={} tblNails={} tblMarkings={} sCncNails="" -- Handle planks tblPlanks=mapElemToPlanks[vlayer.guid] if gtblWupCollectElems then -- Save element layer to last item local tbl tbl={} tbl.elem=vlayer tbl.quant=quant tbl.tblplanks=tblPlanks gtblWupCollectElems[#gtblWupCollectElems].elemlayers[vlayer.type]=tbl end if quant.layertypenum==2 and quant.tblboards and tblPlanks==nil then -- Board should be exploded into planks WupShowErrMsg( string.format("ERROR: PANEL MUST BE EXPLODED CURRENTLY: ID=%s. PANELS NOT WRITTEN", quant.tblboards[1].id) ) end vecNoCuts=nil -- If there is paneling no cuts on x-axis of the panel if tblPlanks then if bCore then tblPlanks=WupDoCoreLayer(tranMaster, vlayer, tblPlanks, tblDrills, tblNails, zLayer, tblMarkings) panelParam=nil -- Append collected MODUL pieces local i, v, dest dest=#tblPlanks for i,v in ipairs(gtblWupModulItems) do dest=dest+1 tblPlanks[dest]=v end else local sPla sPla=sPlaPlank if gsWupIsFraming and gsWupIsFraming~="" then if gsWupIsFraming=="1" then sPla="" end elseif string.match(quant.elemtype, "intstud.*") then sPla="" -- Scondary framing end if sPla and sPla~="" then retVal=1 end tblPlanks=WupDoExtraPlankLayer(tranMaster, vlayer, tblPlanks, tblDrills, tblNails, sPla, zLayer, tblMarkings) panelParam=sPlaPlank end table.sort(tblPlanks, function (n1, n2) -- Put modules last, ""=no module if n1.module~=n2.module then return n1.module0.001 then return n1.x1