• lesle (unregistered) in reply to vtcodger
    vtcodger:
    ***Btw, how may square feet are in one acre?***

    Google says 5280**2 / 640 = 43560.

    For those unaccustomed to English system dementia, that's 5280 feet in a statute mile squared divided by 640 acres in a square mile. You live around here, you get used to this whackiness.

    The earth is flat?

  • Ribbit (unregistered)

    I don't know if anyone else has said it, but you should also worry about the kerf of the blade. So in reality, you don't get two 8 foot boards out of a 16 foot board. It's a physical impossibility. You can get one 8 footer and one 7' 11 5/8" board out of a single 16 footer.

  • Walter Fecces (unregistered)

    Not sure if somebody else already pointed this out...

    The reason the English system uses 12 inches to the foot is because 12 is very useful for construction. It can easily be divided into halves, thirds, fourths, sixths, and so on. If I have an 7 foot span that I want to divide into 6 arches or something, it's 1 foot 2 inches, not some annoying decimal (which medieval carpenters probably didn't know how to use).

    You're out of luck if you want to divide something into fifths of course.

  • mypalmike (unregistered) in reply to Maurits
    Maurits:
    TRWTF (other than Alex's unitophobia) is all the whining about NP-completeness.

    Repeat after me:

    NP-complete problems are not difficult to solve. NP-complete problems are not difficult to solve. NP-COMPLETE PROBLEMS ARE NOT DIFFICULT TO SOLVE.

    They just take a long time for large values of N, that's all.

    Consider how small N is going to be for even the largest, most oddly-shaped house.

    Now consider the power of your CPU. How long will a brute-force search take?

    It could take a long long time. The problem is that brute force often doesn't scale even a little. I wrote perhaps the most naive possible brute force solution in python and posted it earlier. It iterates over all possible partitionings of the inputs. Each new input grows the time exponentially (actually, it follows the bell numbers). Here are some timing values for various input sizes:

    Inputs Time
    ------ ----
    9      0.410s
    10     2.178s
    11     12.820s
    12     83.181s  (over a minute)
    13     558.797s (just under 10 minutes)
    14     3947.477s (just over an hour)
    

    Once you reach 50 inputs, you're talking about years and years.

    That is not to say there's no faster brute force solution, but in general, brute force often doesn't scale to even a moderate number of inputs.

  • Anon (unregistered) in reply to gus
    gus:
    What about the issue of a wall that is like one inch longer than a standard board length? You shouldn't use a 1-inch piece, you should recalculate to see if you can use any other pieces to make a more rational configuration. Also you have to allow for the width of the saw blade, and for 45 degree cuts.

    That's a good point. You should probably have a minimum size for pieces because a 1" splice would look really stupid. It might be better to have a splice in the middle than one right at the end. Not to mention how hard it would be to miter a 1" piece.

  • NJ (unregistered) in reply to Anonymous
    Anonymous:
    I'm from the UK (predominantly metric) but I'm still amazed at how many otherwise intelligent people have this utter inability to work with imperial units. What is so confusing about there being 12 inches to a foot? How can you possibly work in hex (base 16) without being able to handle a simple base 12?
    Yeah, so if I create some new system with base 17 (17 wobs in one wab), followed by 19 (19 wabs in a quattamacca) and 317(317 quatta... ok, you get it), you wouldn't have any problem using that. And remembering that the "wob" of course was ~7.4 inches.

    It's not that your system is so extremely complex that noone else could learn it, we've just never learned it or have been in contact with it. And since it's completely insane, we wouldn't try to learn it either.

    The fact that 12 has more divisors first of all might have worked as an argument like 350 years ago. Second, when do you have exactly one foot of something that you want to cut in three pieces? Because, you know, otherwise, it's not really hard to construct a length in the metric system that has the same property (one good example being 3 meters, which can be cut in three pieces of equal non-irrational length. Interested readers should calculate this themselves.).

  • NJ (unregistered) in reply to NJ

    ...and by "non-irrational" I was of course referring to "integer"...

  • Bringer (unregistered)

    I don't know about everyone else, but I found this article really hard to read. Maybe next time there's an article like this one, there should be a simple translation into metric units next to all the imperial ones.

    E.g. Object 1 is a rectangular prism of 1'(30.5cm) by 1'10" (55.9cm).

  • (cs) in reply to mypalmike
    mypalmike:
    Maurits:
    TRWTF (other than Alex's unitophobia) is all the whining about NP-completeness.

    Repeat after me:

    NP-complete problems are not difficult to solve. NP-complete problems are not difficult to solve. NP-COMPLETE PROBLEMS ARE NOT DIFFICULT TO SOLVE.

    They just take a long time for large values of N, that's all.

    Consider how small N is going to be for even the largest, most oddly-shaped house.

    Now consider the power of your CPU. How long will a brute-force search take?

    It could take a long long time. The problem is that brute force often doesn't scale even a little. I wrote perhaps the most naive possible brute force solution in python and posted it earlier. It iterates over all possible partitionings of the inputs. Each new input grows the time exponentially (actually, it follows the bell numbers). Here are some timing values for various input sizes:

    Inputs Time
    ------ ----
    9      0.410s
    10     2.178s
    11     12.820s
    12     83.181s  (over a minute)
    13     558.797s (just under 10 minutes)
    14     3947.477s (just over an hour)
    

    Once you reach 50 inputs, you're talking about years and years.

    That is not to say there's no faster brute force solution, but in general, brute force often doesn't scale to even a moderate number of inputs.

    Time results for my genetic algorithm Post 296599

    Inputs Time
    ------ ----
    9      0.886s
    10     0.789s
    11     0.658s
    12     0.821s
    13     0.846s
    14     0.828s
    
  • Anonymous (unregistered) in reply to NJ
    NJ:
    Anonymous:
    I'm from the UK (predominantly metric) but I'm still amazed at how many otherwise intelligent people have this utter inability to work with imperial units. What is so confusing about there being 12 inches to a foot? How can you possibly work in hex (base 16) without being able to handle a simple base 12?
    It's not that your system is so extremely complex that noone else could learn it, we've just never learned it or have been in contact with it. And since it's completely insane, we wouldn't try to learn it either.
    If you re-read my post you'll see that I'm from the UK which is metric. So imperial is not my system, I've never really been exposed to it and have never had to work with it. But that doesn't mean I turn into a gibbering moron whenever I need to deal with inches. I've never "learned" imperial (as you put it) but as an intelligent human being I really don't have any trouble in getting my head around the principle of a base 12 unit. Base 12 is not complicated - in fact, it makes a number of calculations a great deal easier than a base 10 system.
  • the beholder (unregistered) in reply to The General
    The General:
    Metric works better for people's height and weight when calculating body-mass index, otherwise feet & inches are indeed the thing for height, and stones & pounds for weight - did I mention about the stones? Eight stones make one hundredweight, i.e. 112lb. Simple. ;)
    I don't think so. If you say someone is 5'7", heck there's a whole inch he could fit into. In metric that same someone is 1.70m (or if you prefer 170cm), and if you don't think it is precise enough you just measure millimeters too. That hypothetical person's height is 170,4cm or 170,402895 if you're feeling picky and your measurement instruments are accurate enough.

    In inches you can have some good precision by using fractions, but it gets somewhat complicated when you have something like 5'7" 73/256.

    And what the heck are those stones? Must be another nonsense measure. Maybe it is based on the king's kidney stone weight?

  • sota (unregistered) in reply to NJ
    NJ:
    (one good example being 3 meters, which can be cut in three pieces of equal non-irrational length. Interested readers should calculate this themselves.).

    I'd like to bring up a point that nobody has mentioned yet. What you're suggesting only works if you discount the width of the saw blade when making the cuts.

  • Anon (unregistered) in reply to NJ
    NJ:
    The fact that 12 has more divisors first of all might have worked as an argument like 350 years ago.

    Actually, the idea dates back to at least the Romans.

  • Bill (unregistered)

    First of all, I didn't read all of the comments, so i apologize if this has been covered already.

    I believe that this is not the bin packing problem. The bin packing problem has stochastic arrivals of need, which is not the case here. We don't have any randomness, this is a deterministic problem for any set of board lengths and any sett of wall measurements.

    Just my $0.02

    Bill

  • (cs) in reply to Azarien
    Azarien:
    What the heck is this text about? The article is too American-centric and too non-native-English-speaker-unfriendly.

    And mud-hut-dweller-unfriendly.

  • aktawa (unregistered)

    I really don't see a (unique) solution for the bonus part. Let's assume you have a single 18" wall. Should the seem be in the middle or close to the corner?

  • Bob The Builder (unregistered) in reply to Ramses So let it be written so let it be done

    I have a standard tract home in Southern California with standard painted trim and there isn't a splice anywhere. Of course, thats cuz my house is so small theres no run > 16'.

    Besides:

    a) Painted trim is usually either low grade pressed or plasticized or fiber board. b) My house uses finger joined pine trim... "a factory splice". Its not really stainable obviously. c) Stainable trim needs to be higher grade solid wood. of course its gonna cost more. d) Home builders mark up EVERYTHING by 50%. I was charged $150 to run an extra RG6 coax from the living room up to the attic with OPEN WALLS. The electrician didn't even have to drill any extra holes or anything. $150 for $6 worth of coax :(.

  • Rudy Dakota (unregistered) in reply to frits

    Actually, here (Netherlands) it 's 2.44 x 1.22 meters. Wouldn't know about the bigger boards. Never realized what that would have been in feet ;-)

  • Rudy Dakota (unregistered) in reply to Rudy Dakota

    That was a reaction to fritz:

    <16 feet = 4.88 meters 8 feet = 2.44 meters

    I'm American, so I have no idea what would be standard board length in the rest of the world. Maybe 5 and 2.5 meters? >

  • Jakuri (unregistered)

    This is challenging!! I ruined my whole weekend for this. I am already starting to call my code another WTF because of a lot of weird bugs.

    Also when "maximium" or "minium" is required in a problem, you can always expect insanely LONG solution time. It would take forever for some combinations to finish in my code.

    But at least it seems to be working for now, so it ships...

    The code is written in Lua. You can see for yourself.

    Just modify the parameters given in the main() function. In the saveResult() function you can set the file name in which the result is saved.

    function appendTable(table1,table2) if type(table1) =="table" and type(table2)=="table" then for k,v in pairs(table1) do table.insert(table2,v) end end end

    --__currentIndent = 0 --__indent = "--|"

    --function getIndent() -- if __currentIndent>=0 then -- local i = 0 -- local s = "" -- while i< __currentIndent do -- s = string.format("%s%s",s,__indent) -- i = i+1 -- end -- return s -- else -- return "" -- end --end

    --function addIndent() -- __currentIndent = __currentIndent+1 --end --function decIndent() -- __currentIndent = __currentIndent-1 --end

    --room is the context of the problem --it contains all the edge(size and quantitiy) that need to be trimmed out function populateRoom(dimensions)

    local room = {}

    for k,v in ipairs(dimensions) do local edgeCollection = {} edgeCollection.size = v table.insert(room,edgeCollection) end return room end

    --copies a room for testing

    function copyRoomPlan(room) local newRoom = {} for k,record in ipairs(room) do local newRecord = {} newRecord.size = record.size newRecord.remaining = record.size newRoom[k] = newRecord end return newRoom end

    function canFitIntoTestRoom(testRoom,len) if (len<=0) then return false end local canDoIt = false for k1,record in ipairs(testRoom) do if record.isBigSize ~=true then --normal situation,since it must be if (len >= record.size) and (record.remaining > 0) then canDoIt = true break end else --big size edge means it must be spliced,so take it easy if record.remaining > 0 then canDoIt = true break end end end return canDoIt end

    --test if two cover plans are equivalent --which means that the two cover the same area of the room function isRoomEquivalentTo(room,test1,test2)

    local isEqual = true
    
    for k,edge in ipairs(test1) do
        local edge2 = test2[k]
        if edge.remaining ~= edge2.remaining then
           isEqual = false
           break
        end
    end
    return isEqual
    

    end

    --get a cached result --the same result has only one instance --this is guranteed by the setStoredResults() function

    function copyResult(result) local newResult = {} for k,v in result do newResult[k] = v end return newResult end

    function getStoredResults(room, cover)

    local newResult =nil
    
    for storRoom,result in pairs(room.storedResults) do
        local covRoom = room:createTestRoom(cover)
        if room:isRoomEquivalentTo(storRoom,covRoom) then
           newResult = copyResult(result)
        end
    end
    
    return newResult
    

    end

    --set a cached result

    function setStoredResults(room,cover,result)

    local covRoom = room:createTestRoom(cover)
    
    for storRoom,result in pairs(room.storedResults) do
        if room:isRoomEquivalentTo(storRoom,covRoom) then
            return false
        end
    end
    
    room.storedResults[covRoom] = copyResult(result)
    return true
    

    end

    --applies a cover to the room --creates a new 'room' for testing further plans

    function createTestRoom(room,cover)

    local i = cover
    local testRoom = copyRoomPlan(room)
    
    --remove the wood already used in the cove plan
    
    while i ~= nil do
       local slices = i.slices
       for k,v in ipairs(slices) do
           local record = testRoom[v.edgeId]
           record.remaining = record.remaining - v.len
       end
       i = i.prev
    end
    
    testRoom.boards = room.boards
    testRoom.orgRoom = room
    testRoom["canFitIntoTestRoom"] = canFitIntoTestRoom
    return testRoom
    

    end

    --test if a cover plan has already coverd all the doors

    function isAllCovered(room, cover)

    local testRoom = room:createTestRoom(cover)
    local isCovered = true
    for k,v in ipairs(testRoom) do
        if v.remaining > 0 then
            isCovered = false
            break
        end
    end    
    return isCovered
    

    end

    --creates a blank proposal with only the 'discarded' part

    local function createDiscard(discLength) local discard={} discard.discard = discLength discard.slices={} return discard end

    function tryOnEdge(testRoom,edgeId,edge,len,cover)

    local nxProposals = nil
    
    local function addSlice(prop,cutLength)
        if(prop~=nil) then
            for k,cov in pairs(prop) do
                local newSlice = {}
                newSlice.edgeId = edgeId
                newSlice.len = cutLength
                table.insert(cov.slices,newSlice)
            end
        end
    end
    
    --if the length can fully cover this edge, try the remaining length with 
    --other edges
    
    if (len>= edge.size) and (edge.remaining>0) then
        local nxLen = len - edge.size
        local edgeRemaining = edge.remaining
        edge.remaining = edge.remaining - edge.size
        nxProposals = makePoposalInTestRoom(testRoom,cover,nxLen,edgeId)
        addSlice(nxProposals,len - nxLen)
        edge.remaining = edgeRemaining
    else
        --if the length cannot fully cover the edge
        nxProposals = {}
        --when there is no place for the piece to go ,discard it
        --but if the discarded length equals the whole length of the piece
        --do not create a discard(because that changes nothing)
        if  testRoom:canFitIntoTestRoom(len)~=true and
            testRoom.orgLength > len then
    
            table.insert(nxProposals,createDiscard(len))
        end
    end
    return nxProposals
    

    end

    function makePoposalInTestRoom(testRoom,cover,len,startEdge)

    local proposal = {}
    
    for edgeId,edge in ipairs(testRoom) do
        if edgeId >= startEdge then
            local nProposals = tryOnEdge(testRoom,edgeId,edge,len,cover)
            if nProposals ~=nil then
                appendTable(nProposals,proposal)
            end
        end
    end
    
    --TODO: merge redundant blank entries
    
    local mergedProposal = {}
    local mergeFlag = {}
    
    for k,v in ipairs(proposal) do
        if table.getn(v.slices)>0 then
            table.insert(mergedProposal,v)
        else
            if mergeFlag[v.discard] ~= true then
                table.insert(mergedProposal,v)
                mergeFlag[v.discard] = true
            end
        end
    end
    proposal = mergedProposal
    return proposal
    

    end

    function makeProposals(room, cover, len)

    local testRoom = room:createTestRoom(cover)
    testRoom.orgLength = len
    local proposals = makePoposalInTestRoom(testRoom,cover,len,1)
    
    --add information of original size into proposals
    
    for k,v in ipairs(proposals) do
        v.orgSize = len
    end
    return proposals
    

    end

    function evalProposal(room, cover, proposal)

    --temporally links the current proposal with the cover.
    --this cover will be used through the whole process
    proposal.prev = cover
    
    local depthP = cover
    local depth = 0
    while depthP ~=nil do
    
        depthP = depthP.prev
        depth = depth+1
    end
    print("Eval",depth);
    
    --recursivly solves the problem
    local result = room:solve(proposal)
    
    --calc the cost of current cover. add it to the result
    
    result.discardedWood =
        result.discardedWood + proposal.discard
    if result[proposal.orgSize] ==nil then
       result[proposal.orgSize] =1
    else
       result[proposal.orgSize] =
           result[proposal.orgSize] + 1
    end
        
    --merge current cover plan to the result
    local resultPath = result.path
    result.path = proposal
    proposal.next = resultPath
    
    --store the result/cover pair for later reference
    
    room:setStoredResults(proposal,result)
    
    proposal.prev = nil
    
    if result ~=nil then
        local i = result.path
        local cnt = 0
        while i~=nil do
            cnt = cnt + 1
            i = i.next
        end
        print("ResultLevel:",cnt)
    end
    
    print("Eval",depth,"End")
    return result
    

    end

    function getTotalBoards(result) local sum = 0 for k,v in pairs(result) do if type(k) == "number" then sum = sum+v end end return sum end

    --if res1 is fitter than res2 than returns true, else returns false

    function isBetterChoiceThan(res1,res2) if(res2==nil) then return true else if getTotalBoards(res1) < getTotalBoards(res2) then return true else return false end end end

    function solve(room, cover)

    if room:isAllCovered(cover) then
        local r = {}
        r.discardedWood = 0
        return r
    else
        --fetch a result from cache, if the result is already cached, than
        --return it instead
        local result = room:getStoredResults(cover)
        if(result ~=nil) then
        else
            for k1,size in ipairs(room.boards) do
                
                local nxProposals = room:makeProposals(cover,size)
                
                for k2,proposal in ipairs(nxProposals) do
                    local resultCandidate = room:evalProposal(cover,proposal)
                    if isBetterChoiceThan(resultCandidate,result) then
                        result = resultCandidate
                    end 
                end
            end
        end
        
        return result
    end
    

    end

    function Desc(e1,e2) if e1 > e2 then return true else return false end end

    function createRoom(demensions, boards) local room = populateRoom(demensions) room["storedResults"] = {} table.sort(boards,Desc) room["boards"] = boards room["cntDims"] = table.getn(demensions) room["solve"] = solve room["isAllCovered"] = isAllCovered room["makeProposals"] = makeProposals room["evalProposal"] = evalProposal room["getStoredResults"] = getStoredResults room["setStoredResults"] = setStoredResults room["createTestRoom"] = createTestRoom room["isRoomEquivalentTo"] = isRoomEquivalentTo

    return room    
    

    end

    --outputs the result to a file

    function saveResult(result)

    io.output("c:/result.txt")

    io.write(string.format("discardedWood=%d",result.discardedWood),"\n") io.write("casings used:\n") for k,v in pairs(result) do if type(k) == "number" then io.write(string.format("%d inch:%d\n",k,v)) end end

    io.write("cuts:\n");

    local path = result.path local id = 1

    while path~=nil do io.write(string.format("%d:length = %d, discard = %d\n", id,path.orgSize,path.discard)); for k,v in ipairs(path.slices) do io.write(string.format(" %d:length = %d,edgeId = %d", k,v.len,v.edgeId)) end io.write("\n") id = id + 1 path = path.next end io.write("----------------\n")

    io.flush()

    end

    function main()

    local d = {}
    d[1] = 32
    d[2] = 84
    d[3] = 84
    d[4] = 120
    d[5] = 150
    d[6] =40
    
    local room = createRoom(d, {160})
    
    local voidCover = {}
    voidCover.slices = {}
    
    result = room:solve(voidCover)
    saveResult(result)
    
    print("--------------------------------")
    

    end

    main()

  • LB (unregistered) in reply to Steve the Cynic
    Steve the Cynic:
    Jamiec:
    So for all thge wisdom of the metric system, it seems that we sell boards in quasi-imperial, metrically measured lengths.
    Not just wood, either. In British supermarkets, beer in bottles is often sold in units of 568ml.

    That's "pints" to old farts like me.

    That's sort of how it works in the US as well. The US has been officially metric for a long time, but that fact is largely ignored. For instance, the soda can I'm drinking from now is labeled as being "12 FL OZ (355 mL)". The only part of that that's legally required to be on the label is the "355 mL", but it's known as a twelve ounce can. (Of course, just to keep things inconsistent, we can also buy soda in two liter bottles.)
  • Jonathan (unregistered)

    /* Disclaimer: I haven't coded anything in months, and I did not compile or test this in any way. It probably doesn't work at all. */

    int boards(int[] lengths, int boardLen) { int i=0; int boards=0; while(lengths[i]!=0) { if(lengths[i] > boardLen) { boards+=lengths[i]/boardLen; lengths[i]=lengths[i]%boardLen; }

    if(lengths[j] != -1)
    {
      int j=i;
      int tot=0;
      while(lengths[j] != 0)
      {
        if(lengths[j] != -1 && tot+lengths[j] <= boardLen)
        {
          tot+=lengths[j];
          lengths[j]=-1;
        }
      }
      boards++
    }
    i++;
    

    } return boards; }

  • (cs)

    I know there's some debate on this among finish carpenters (some of whom prefer glued, or even biscuited or doweled, butt joints), but I'm surprised no one has mentioned scarf joints in the comments. Yes, with stained trim a scarf joint would likely be visible - but many people consider that acceptable, if the joint locations are reasonable. (That leaves out most door and window casings, of course.)

    Personally, I don't mind reasonable scarfs in my baseboards and chair rails. And there's actually one in one of my door casings, though that was to preserve some of the hundred-year-old original red oak trim in the house, after it had been hacked up by previous owners. My crown mouldings are mostly tin (or terne, or some other alloy), so joinery for them is somewhat different.

    Of course, allowing some unnecessary scarfs just relaxes some of the constraints of the problem.

    And for runs longer than the maximum board length, you sometimes want extra joints, for example to avoid a joint over a prominent visual feature like a door.

  • Anonymous Coward (unregistered) in reply to Azarien
    Azarien:
    What the heck is this text about? The article is too American-centric and too non-native-English-speaker-unfriendly.

    You people ruined en.wikipedia with your POV boxes

  • waytoolate (unregistered)

    I know this is way too late, but here is the easy solution in 36 lines in vbscript. No compling needed for a windows box, just save as whatever.vbs and double-click for the hotness. Sorry guys, didn't have any compilers at my disposal when I read this. Just good ol' edit.

    dim store() redim store(0) do until DONE = True strinput = Inputbox ("Oh hai! I heard you like boards." & vbnewline & "Enter your measurement in inches or NONE MORE when done.") select case ucase(strinput) case "NONE MORE" DONE = True case else if not store(ubound(store)) = "" then redim preserve store(ubound(store) + 1) end if store(ubound(store)) = strinput end select Loop dim scrap() redim scrap(0) scrap(0) = 192 boardcount = 1 broken = False for i = 0 to ubound(store) for j = 0 to ubound(scrap) if int(store(i)) < int(scrap(j)) then scrap(j) = scrap(j) - store(i) broken = True exit for end if next if broken = False then boardcount = boardcount + 1 redim scrap(ubound(scrap) + 1) scrap(ubound(scrap)) = 192 i = i - 1 end if broken = False next msgbox("ALL DONES!!" & vbnewline & "You will need -" & boardcount & "- 16 foot boards")

  • waytoolate (unregistered) in reply to waytoolate

    d'oh... and here's it acutally working. Added 4 lines, bringing it up to 40.

    dim store() redim store(0) do until DONE = True strinput = Inputbox ("Oh hai! I heard you like boards." & vbnewline & "Enter measure in inches or NONE MORE when done.") select case ucase(strinput) case "NONE MORE" DONE = True case else if not store(ubound(store)) = "" then redim preserve store(ubound(store) + 1) end if store(ubound(store)) = strinput end select Loop dim scrap() redim scrap(0) scrap(0) = 192 boardcount = 1 broken = False for i = 0 to ubound(store) for j = 0 to ubound(scrap) if int(store(i)) < int(scrap(j)) then scrap(j) = scrap(j) - store(i) broken = True exit for end if next if broken = False then boardcount = boardcount + 1 if int(store(i)) < 192 then redim scrap(ubound(scrap) + 1) scrap(ubound(scrap)) = 192 else store(i) = store(i) - 192 end if i = i - 1 end if broken = False next msgbox("ALL DONES!!" & vbnewline & "You will need -" & boardcount & "- 16 foot boards")

  • Friday (unregistered)

    Here's the math that took me all of a minute to think out: floor(lengthofboard/sumofneededunsplicedlengthperobject)*(number_of_these_objects)

  • steve (unregistered)

    <a rel="nofollow" href="data:text/html,<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><title>Board calculator</title><style type="text/css">select{text-align:right;float:left;width:100px}.btn{width:25pt;margin:0px;position:relative;top:-2px}div{float:left}p{clear:left}</style><script type="text/javascript">function S(e,E){if(typeof E==U||E.ctrlKey&&e.keyCode==65){for(i=0;i<e.options.length;++i)e.options[i%5D.selected=T;e.onchange();return F}k=E.keyCode;if(k==45)A(e);if(k==46)D(e);return T}function A(e,n,s,z){if(typeof n==U||n==null){n=prompt("Enter length(s) in "+(typeof z!=U&&z!=null&&z==x%3F"inches":"feet")+":","");if(n==null||n<=0)return;n=n.replace(/[\s,%5D+/g,",").split(",")}for(var O,i,o=0,r=s!="desc";o<n.length;++o){n[o%5D=h(n[o%5D,z);if(!isNaN(n[o%5D)&&n[o%5D>0){with(O=document.createElement("option")){value=n[o%5D;innerHTML=g(n[o%5D,z)}if(typeof s==U||s==null)e.appendChild(O);else{p=e.options;for(i=0;i<p.length;++i)if((parseFloat(p[i%5D.value)-n[o%5D)(r%3F-1:1)<=0)break;if(p.length==0||i>=p.length)e.appendChild(O);else if((parseFloat(p[i%5D.value)-n[o%5D)(r%3F-1:1)<0)e.insertBefore(O,p[i%5D)}}}e.onchange()}function D(e){for(var i=e.options.length-1;i>=0;--i)if(e.options[i%5D.selected)e.removeChild(e.options[i%5D);e.onchange()}function M(e,s,z,n,b){if(typeof n!=U&&n!=null)z.value=n;else if(z.value=="")z.value=y;else z.value=(z.value==y%3Fw:z.value==w%3Fx:y);b.value=(z.value.indexOf(w)>=0%3Fw+String.fromCharCode(rsq):"")+(z.value.indexOf(x)>=0%3Fx+String.fromCharCode(rdq):"");P(e,s,z.value)}function P(e,s,z){for(var l=[%5D,i=0;i<e.options.length;++i)l.push(parseFloat(e.options[i%5D.value));S(e);D(e);A(e,l,s,z)}function n(e){r=[%5D;for(i=0;i<e.options.length;++i)r.push(e.options[i%5D.value);return r.join()}function h(s,z){if(typeof s=="string"){m=s.replace(/[\u2018\u2019%5D/g,"'").replace(/[\u201C\u201D%5D/g,'"').match(/['"%5D|[\d%5D+[.%5D[\d%5D|[\d%5D[.%5D[\d%5D+|[\d%5D+/g);s=0;if(m==null)return 0;if(m.length%252>0&&(typeof z==U||z.indexOf(w)>=0))m.push("'");while(m.length>0)s+=parseFloat(m.shift())/(m.shift()!="'"%3F12:1)}return parseFloat(s)}function g(l,z){if(typeof z==U)z=y;if(typeof l!="number")l=h(l,z);if(z==w)return Math.round(l1000)/1000+"&%23"+rsq+";";if(z==x)return Math.round(l120)/10+"&%23"+rdq+";";var ft=Math.floor(l);if(ft==l)return ft+"&%23"+rsq+";";if(ft==0)return Math.round(l120)/10+"&%23"+rdq+";";return ft+"&%23"+rsq+";"+Math.round((l-ft)120)/10+"&%23"+rdq+";"}function Q(c,d,z,Z){u=[%5D;R=c;c=[%5D;p=R.options;for(v=m=i=0;i<p.length;c.push(parseFloat(p[i++%5D.value)));R=d;p=R.options;d=[%5D;for(i=0;i<p.length;++i){d.push(parseFloat(p[i%5D.value));if(d[i%5D>d[v%5D)v=d[i%5D;u.push([%5D)}if(d.length>0){while(c.length>0){var b=c.shift(),f=T;while(b>d[v%5D){++m;u[v%5D.push([F%5D);if(f){u[v%5D[u[v%5D.length-1%5D.push(b);f=F}b-=d[v%5D}for(var i=0,f=F;i<u.length;++i){for(j=0;j<u[i%5D.length;++j){if(u[i%5D[j%5D[u[i%5D[j%5D.length-1%5D!==F&&d[i%5D-u[i%5D[j%5D[u[i%5D[j%5D.length-1%5D>=b){f=T;break}}if(f)break}if(f)u[i%5D[j%5D.push(u[i%5D[j%5D[u[i%5D[j%5D.length-1%5D+b);else for(var i=0;i<d.length;++i)if(d[i%5D>=b){++m;u[i%5D.push([b%5D);break}}}o=document.getElementById("_"),dir=d[bf=s=0%5D>d[d.length-1%5D;o.innerHTML="";if(m>0){for(i=(dir%3F0:d.length-1);i>=0&&i<d.length;i+=(dir%3F1:-1))if(u[i%5D.length>0){p=r="";for(m=j=0;j<u[i%5D.length;++j){if(u[i%5D[j%5D[0%5D==F){++m;if(u[i%5D[j%5D.length>1)p+="Make "+g(u[i%5D[j%5D[1%5D,z)+" by splicing together "+(Math.floor(u[i%5D[j%5D[1%5D/d[i%5D)>1%3FMath.floor(u[i%5D[j%5D[1%5D/d[i%5D)+" boards":"a board")+" and a "+g(u[i%5D[j%5D[1%5D%25d[i%5D,z)+" piece
    "}else if(dir&&i<d.length-1&&u[i%5D[j%5D[u[i%5D[j%5D.length-1%5D<d[i+1%5D){u[i+1%5D.push(u[i%5D[j%5D)}else if(!dir&&i>0&&u[i%5D[j%5D[u[i%5D[j%5D.length+1%5D<d[i-1%5D){u[i-1%5D.push(u[i%5D[j%5D)}else{++m;var t=[%5D,c=[%5D;for(var k=0;k<u[i%5D[j%5D.length;++k){if(u[i%5D[j%5D[k%5D<d[i%5D)t.push(g(u[i%5D[j%5D[k%5D,z));if(k>0)c.push(g(u[i%5D[j%5D[k%5D-u[i%5D[j%5D[k-1%5D,z));else c.push(t[t.length-1%5D)}if(t.length==2)t=t.join(" and ");else{if(t.length>1)t[t.length-1%5D="and "+t[t.length-1%5D;t=t.join(", ")}if(c.length==2)c=c.join(" and ")+" pieces";else{if(c.length>1){c[c.length-1%5D="and "+c[c.length-1%5D+" pieces";c=c.join(", ")}else c="a "+c.join()+" piece"}r+="Cut at "+t+" to make "+c+(u[i%5D[j%5D[u[i%5D[j%5D.length-1%5D<d[i%5D%3F" and a "+g(d[i%5D-u[i%5D[j%5D[u[i%5D[j%5D.length-1%5D,z)+" scrap":"")+"
    ";s+=d[i%5D-u[i%5D[j%5D[u[i%5D[j%5D.length-1%5D}}if(m>0)o.innerHTML+="
    Number of "+g(d[i%5D,Z)+" boards required: "+m+"

    "+r+p;bf+=d[i%5D
    m}o.innerHTML+="
    "+g(bf,Z)+" of trim required
    "+g(s,z)+" of scrap ("+Math.round(s/bf
    1000)/10+"%25)"}}</script></head><body onload="U=typeof a;T=!0;F=!T;w='ft';x='in';y=w+x;f=document.forms[0%5D;A(f.C,f.G.value.split(','),null,w);A(f.H,f.I.value.split(','),f.K.value,w);M(f.C,null,f.J,f.J.value>''%3Ff.J.value:x,f.N);M(f.H,f.K.value,f.L,f.L.value>''%3Ff.L.value:y,f.B);Q(f.C,f.H,f.J.value,f.L.value);"><form>

    Req<script type="text/javascript">rsq=8217;rdq=8221;if(navigator.appVersion.indexOf("Win")<0){rsq=39;rdq=34}document.write("&%23",rsq,";");</script>d Lengths:
    <input type="hidden" name="G" value=""/><select name="C" size="5" multiple onkeydown="return S(this,event);" onchange="for(var i=s=0;i<this.options.length;s|=this.options[i++%5D.selected); f.V.disabled=!s;f.G.value=n(this); Q(f.C,f.H,f.J.value,f.L.value);"></select><input type="button" value="+" class="btn" onclick="A(f.C,null,null,f.J.value);"/>
    <input type="button" name="V" value="-" class="btn" onclick="D(f.C);"/>
    <input type="hidden" name="J"/><input type="button" name="N" class="btn" value=" " onclick="M(f.C,null,f.J,null,this);"/>
       
    Board Lengths:
    <input type="hidden" name="I" value="16,8"/><select name="H" size="5" multiple onkeydown="return S(this,event);" onchange="for(var i=s=0;i<this.options.length;s|=this.options[i++%5D.selected);f.W.disabled=!s;f.I.value=n(this);Q(f.C,f.H,f.J.value,f.L.value);"></select><input type="button" value="+" class="btn" onclick="A(f.H,null,f.K.value,f.L.value);"/>
    <input type="button" name="W" value="-" class="btn" onclick="D(f.H);"/>
    <input type="hidden" name="L"/><input type="button" name="B" class="btn" value=" " onclick="M(f.H,f.K.value,f.L,null,this);"/>
    <input type="hidden" name="K" value="desc"/><input type="button" name="rev" value="%25" class="btn" onclick="f.K.value=f.K.value=='desc'%3F'asc':'desc';P(f.H,f.K.value,f.L.value)"/>

    </form></body></html>" target="blank" title="data:text/html,<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><title>Board calculator</title><style type="text/css">select{text-align:right;float:left;width:100px}.btn{width:25pt;margin:0px;position:relative;top:-2px}div{float:left}p{clear:left}</style><script type="text/javascript">function S(e,E){if(typeof E==U||E.ctrlKey&&e.keyCode==65){for(i=0;i<e.options.length;++i)e.options[i%5D.selected=T;e.onchange();return F}k=E.keyCode;if(k==45)A(e);if(k==46)D(e);return T}function A(e,n,s,z){if(typeof n==U||n==null){n=prompt("Enter length(s) in "+(typeof z!=U&&z!=null&&z==x%3F"inches":"feet")+":","");if(n==null||n<=0)return;n=n.replace(/[\s,%5D+/g,",").split(",")}for(var O,i,o=0,r=s!="desc";o<n.length;++o){n[o%5D=h(n[o%5D,z);if(!isNaN(n[o%5D)&&n[o%5D>0){with(O=document.createElement("option")){value=n[o%5D;innerHTML=g(n[o%5D,z)}if(typeof s==U||s==null)e.appendChild(O);else{p=e.options;for(i=0;i<p.length;++i)if((parseFloat(p[i%5D.value)-n[o%5D)(r%3F-1:1)<=0)break;if(p.length==0||i>=p.length)e.appendChild(O);else if((parseFloat(p[i%5D.value)-n[o%5D)(r%3F-1:1)<0)e.insertBefore(O,p[i%5D)}}}e.onchange()}function D(e){for(var i=e.options.length-1;i>=0;--i)if(e.options[i%5D.selected)e.removeChild(e.options[i%5D);e.onchange()}function M(e,s,z,n,b){if(typeof n!=U&&n!=null)z.value=n;else if(z.value=="")z.value=y;else z.value=(z.value==y%3Fw:z.value==w%3Fx:y);b.value=(z.value.indexOf(w)>=0%3Fw+String.fromCharCode(rsq):"")+(z.value.indexOf(x)>=0%3Fx+String.fromCharCode(rdq):"");P(e,s,z.value)}function P(e,s,z){for(var l=[%5D,i=0;i<e.options.length;++i)l.push(parseFloat(e.options[i%5D.value));S(e);D(e);A(e,l,s,z)}function n(e){r=[%5D;for(i=0;i<e.options.length;++i)r.push(e.options[i%5D.value);return r.join()}function h(s,z){if(typeof s=="string"){m=s.replace(/[\u2018\u2019%5D/g,"'").replace(/[\u201C\u201D%5D/g,'"').match(/['"%5D|[\d%5D+[.%5D[\d%5D|[\d%5D[.%5D[\d%5D+|[\d%5D+/g);s=0;if(m==null)return 0;if(m.length%252>0&&(typeof z==U||z.indexOf(w)>=0))m.push("'");while(m.length>0)s+=parseFloat(m.shift())/(m.shift()!="'"%3F12:1)}return parseFloat(s)}function g(l,z){if(typeof z==U)z=y;if(typeof l!="number")l=h(l,z);if(z==w)return Math.round(l1000)/1000+"&%23"+rsq+";";if(z==x)return Math.round(l120)/10+"&%23"+rdq+";";var ft=Math.floor(l);if(ft==l)return ft+"&%23"+rsq+";";if(ft==0)return Math.round(l*120)/10+"&%23"+rdq+";";return ft+"&%23"+rsq+";"+Math.round((l-ft)*120)/10+"&%23"+rdq+";"}function Q(c,d,z,Z){u=[%5D;R=c;c=[%5D;p=R.options;for(v=m=i=0;i<p.length;c.push(parseFloat(p[i++%5D.value)));R=d;p=R.options;d=[%5D;for(i=0;i<p.length;++i){d.push(parseFloat(p[i%5D.value));if(d[i%5D>d[v%5D)v=d[i%5D;u.push([%5D)}if(d.length>0){while(c.length>0){var b=c.shift(),f=T;while(b>d[v%5D){++m;u[v%5D.push([F%5D);if(f){u[v%5D[u[v%5D.length-1%5D.push(b);f=F}b-=d[v%5D}for(var i=0,f=F;i<u.length;++i){for(j=0;j<u[i%5D.length;++j){if(u[i%5D[j%5D[u[i%5D[j%5D.length-1%5D!==F&&d[i%5D-u[i%5D[j%5D[u[i%5D[j%5D.length-1%5D>=b){f=T;break}}if(f)break}if(f)u[i%5D[j%5D.push(u[i%5D[j%5D[u[i%5D[j%5D.length-1%5D+b);else for(var i=0;i<d.length;++i)if(d[i%5D>=b){++m;u[i%5D.push([b%5D);break}}}o=document.getElementById(""),dir=d[bf=s=0%5D>d[d.length-1%5D;o.innerHTML="";if(m>0){for(i=(dir%3F0:d.length-1);i>=0&&i<d.length;i+=(dir%3F1:-1))if(u[i%5D.length>0){p=r="";for(m=j=0;j<u[i%5D.length;++j){if(u[i%5D[j%5D[0%5D==F){++m;if(u[i%5D[j%5D.length>1)p+="Make "+g(u[i%5D[j%5D[1%5D,z)+" by splicing together "+(Math.floor(u[i%5D[j%5D[1%5D/d[i%5D)>1%3FMath.floor(u[i%5D[j%5D[1%5D/d[i%5D)+" boards":"a board")+" and a "+g(u[i%5D[j%5D[1%5D%25d[i%5D,z)+" piece
    "}else if(dir&&i<d.length-1&&u[i%5D[j%5D[u[i%5D[j%5D.length-1%5D<d[i+1%5D){u[i+1%5D.push(u[i%5D[j%5D)}else if(!dir&&i>0&&u[i%5D[j%5D[u[i%5D[j%5D.length+1%5D<d[i-1%5D){u[i-1%5D.push(u[i%5D[j%5D)}else{++m;var t=[%5D,c=[%5D;for(var k=0;k<u[i%5D[j%5D.length;++k){if(u[i%5D[j%5D[k%5D<d[i%5D)t.push(g(u[i%5D[j%5D[k%5D,z));if(k>0)c.push(g(u[i%5D[j%5D[k%5D-u[i%5D[j%5D[k-1%5D,z));else c.push(t[t.length-1%5D)}if(t.length==2)t=t.join(" and ");else{if(t.length>1)t[t.length-1%5D="and "+t[t.length-1%5D;t=t.join(", ")}if(c.length==2)c=c.join(" and ")+" pieces";else{if(c.length>1){c[c.length-1%5D="and "+c[c.length-1%5D+" pieces";c=c.join(", ")}else c="a "+c.join()+" piece"}r+="Cut at "+t+" to make "+c+(u[i%5D[j%5D[u[i%5D[j%5D.length-1%5D<d[i%5D%3F" and a "+g(d[i%5D-u[i%5D[j%5D[u[i%5D[j%5D.length-1%5D,z)+" scrap":"")+"
    ";s+=d[i%5D-u[i%5D[j%5D[u[i%5D[j%5D.length-1%5D}}if(m>0)o.innerHTML+="
    Number of "+g(d[i%5D,Z)+" boards required: "+m+"

    "+r+p;bf+=d[i%5Dm}o.innerHTML+="
    "+g(bf,Z)+" of trim required
    "+g(s,z)+" of scrap ("+Math.round(s/bf
    1000)/10+"%25)"}}</script></head><body onload="U=typeof a;T=!0;F=!T;w='ft';x='in';y=w+x;f=document.forms[0%5D;A(f.C,f.G.value.split(','),null,w);A(f.H,f.I.value.split(','),f.K.value,w);M(f.C,null,f.J,f.J.value>''%3Ff.J.value:x,f.N);M(f.H,f.K.value,f.L,f.L.value>''%3Ff.L.value:y,f.B);Q(f.C,f.H,f.J.value,f.L.value);"><form>
    Req<script type="text/javascript">rsq=8217;rdq=8221;if(navigator.appVersion.indexOf("Win")<0){rsq=39;rdq=34}document.write("&%23",rsq,";");</script>d Lengths:
    <input type="hidden" name="G" value=""/><select name="C" size="5" multiple onkeydown="return S(this,event);" onchange="for(var i=s=0;i<this.options.length;s|=this.options[i++%5D.selected); f.V.disabled=!s;f.G.value=n(this); Q(f.C,f.H,f.J.value,f.L.value);"></select><input type="button" value="+" class="btn" onclick="A(f.C,null,null,f.J.value);"/>
    <input type="button" name="V" value="-" class="btn" onclick="D(f.C);"/>
    <input type="hidden" name="J"/><input type="button" name="N" class="btn" value=" " onclick="M(f.C,null,f.J,null,this);"/>
       
    Board Lengths:
    <input type="hidden" name="I" value="16,8"/><select name="H" size="5" multiple onkeydown="return S(this,event);" onchange="for(var i=s=0;i<this.options.length;s|=this.options[i++%5D.selected);f.W.disabled=!s;f.I.value=n(this);Q(f.C,f.H,f.J.value,f.L.value);"></select><input type="button" value="+" class="btn" onclick="A(f.H,null,f.K.value,f.L.value);"/>
    <input type="button" name="W" value="-" class="btn" onclick="D(f.H);"/>
    <input type="hidden" name="L"/><input type="button" name="B" class="btn" value=" " onclick="M(f.H,f.K.value,f.L,null,this);"/>
    <input type="hidden" name="K" value="desc"/><input type="button" name="rev" value="%25" class="btn" onclick="f.K.value=f.K.value=='desc'%3F'asc':'desc';P(f.H,f.K.value,f.L.value)"/>

    </form></body></html>">data:...

    <!DOCTYPE html>
    <html>
    <head>
    <meta</span> http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title>Board calculator</title>
    <style</span> type="text/css">
    select {
        text-align: right;
        float: left;
        width: 100px;
    }
    
    .btn {
        width: 25pt;
        margin: 0px;
        position: relative;
        top: -2px;
    }
    
    div {
        float: left;
    }
    
    p {
        clear: left;
    }
    </style>
    

    <script type="text/javascript"> var rsq = 8217, rdq = 8221; if (navigator.appVersion.indexOf("Win") < 0) { rsq = 39; rdq = 34; }

    function selectAll(e, event) { if (typeof event == "undefined" || event.ctrlKey && event.keyCode == 65) { for (var i = 0; i < e.options.length; ++ i) e.options[i].selected = true; e.onchange(); return false; } else if (event.keyCode == 45) addLength(e); else if (event.keyCode == 46) delLength(e); return true; }

    function addLength(e, len, sort, fmt) { if (typeof len == "undefined" || len == null) { len = prompt("Enter length(s) in " + (typeof fmt != "undefined" && fmt != null && fmt == "in" ? "inches" : "feet") + ":", ""); if (len == null || len <= 0) return; len = len.replace(/[\s,]+/g, ",").split(","); }

    <span style="color:#000080;"><b><i>for</i></b></span> (<span style="color:#000080;"><b><i>var</i></b></span> option, i, o = <span style="color:red;">0</span>, rev = sort != <span style="color:#808080;">"desc"</span>; o < len.length; ++ o) {
        len[o] = parseFtIn(len[o], fmt);
        <span style="color:#000080;"><b><i>if</i></b></span> (!isNaN(len[o]) && len[o] > <span style="color:red;">0</span>) {
            <span style="color:#000080;"><b><i>with</i></b></span> (option = document.createElement(<span style="color:#808080;">"option"</span>)) {
                value = len[o];
                innerHTML = formatFtIn(len[o], fmt);
            }
            
            <span style="color:#000080;"><b><i>if</i></b></span> (<span style="color:#000080;"><b><i>typeof</i></b></span> sort == <span style="color:#808080;">"undefined"</span> || sort == null)
                e.appendChild(option);
            <span style="color:#000080;"><b><i>else</i></b></span> {
                <span style="color:#000080;"><b><i>for</i></b></span> (i = <span style="color:red;">0</span>; i < e.options.length; ++ i)
                    <span style="color:#000080;"><b><i>if</i></b></span> ((parseFloat(e.options[<i></i>i].value) - len[o]) * (rev ? -<span style="color:red;">1</span> : <span style="color:red;">1</span>) <= <span style="color:red;">0</span>)
                        <span style="color:#000080;"><b><i>break</i></b></span>;
                
                <span style="color:#000080;"><b><i>if</i></b></span> (e.options.length == <span style="color:red;">0</span> || i >= e.options.length)
                    e.appendChild(option);
                <span style="color:#000080;"><b><i>else if</i></b></span> ((parseFloat(e.options[<i></i>i].value) - len[o]) * (rev ? -<span style="color:red;">1</span> : <span style="color:red;">1</span>) < <span style="color:red;">0</span>)
                    e.insertBefore(option, e.options[<i></i>i]);
            }
        }
    }
    
    e.onchange();
    

    }

    function delLength(e) { for (var i = e.options.length - 1; i >= 0; -- i) if (e.options[i].selected) e.removeChild(e.options[i]); e.onchange(); }

    function reverse(e, s, fmt) { //reverse the sort direction s.value = s.value == "desc" ? "asc" : "desc"; repopulate(e, s.value, fmt); }

    function changeFmt(e, s, fmt, newfmt, btn) { if (typeof newfmt != "undefined" && newfmt != null) fmt.value = newfmt; else if (fmt.value == "") fmt.value = "ftin"; else fmt.value = (fmt.value == "ftin" ? "ft" : fmt.value == "ft" ? "in" : "ftin");

    btn.value = (fmt.value.indexOf(<span style="color:#808080;">"ft"</span>) >= <span style="color:red;">0</span> ? <span style="color:#808080;">"ft"</span> + String.fromCharCode(rsq) : <span style="color:#808080;">""</span>)
        + (fmt.value.indexOf(<span style="color:#808080;">"in"</span>) >= <span style="color:red;">0</span> ? <span style="color:#808080;">"in"</span> + String.fromCharCode(rdq) : <span style="color:#808080;">""</span>);
    
    repopulate(e, s, fmt.value);
    

    }

    function repopulate(e, s, fmt) { //put all lengths into an array for (var l = [], i = 0; i < e.options.length; ++ i) l.push(parseFloat(e.options[i].value)); //clear the list selectAll(e); delLength(e); //insert all lengths back into list addLength(e, l, s, fmt); }

    function list(e) { for (var r = [], i = 0; i < e.options.length; ++ i) r.push(e.options[i].value); return r.join(); }

    function parseFtIn(s, fmt) { //converts string in the form ft'in" into a floating-point number of feet //if the argument is not a string, it passes it through parseFloat() if (typeof s == "string") { var m = s.replace(/[\u2018\u2019]/g, "'") .replace(/[\u201C\u201D]/g, """) .match(/['"]|[\d]+[.][\d]|[\d][.][\d]+|[\d]+/g); s = 0; if (m == null) return 0; if (m.length % 2 > 0 && (typeof fmt == "undefined" || fmt.indexOf("ft") >= 0)) m.push("'"); while (m.length > 0) s += parseFloat(m.shift()) / (m.shift() != "'" ? 12 : 1); } return parseFloat(s); }

    function formatFtIn(l, fmt) { //converts a floating-point number of feet into ft'in" if (typeof fmt == "undefined") fmt = "ftin";

    <span style="color:#000080;"><b><i>if</i></b></span> (<span style="color:#000080;"><b><i>typeof</i></b></span> l != <span style="color:#808080;">"number"</span>) l = parseFtIn(l, fmt);
    <span style="color:#000080;"><b><i>if</i></b></span> (fmt == <span style="color:#808080;">"ft"</span>) <span style="color:#000080;"><b><i>return</i></b></span> Math.round(l * <span style="color:red;">1000</span>) / <span style="color:red;">1000</span> + <span style="color:#808080;">"&#"</span> + rsq + <span style="color:#808080;">";"</span>;
    <span style="color:#000080;"><b><i>if</i></b></span> (fmt == <span style="color:#808080;">"in"</span>) <span style="color:#000080;"><b><i>return</i></b></span> Math.round(l * <span style="color:red;">120</span>) / <span style="color:red;">10</span> + <span style="color:#808080;">"&#"</span> + rdq + <span style="color:#808080;">";"</span>;
    
    <span style="color:#000080;"><b><i>var</i></b></span> ft = Math.floor(l);
    <span style="color:#000080;"><b><i>if</i></b></span> (ft == l) <span style="color:#000080;"><b><i>return</i></b></span> ft + <span style="color:#808080;">"&#"</span> + rsq + <span style="color:#808080;">";"</span>;
    <span style="color:#000080;"><b><i>if</i></b></span> (ft == <span style="color:red;">0</span>) <span style="color:#000080;"><b><i>return</i></b></span> Math.round(l * <span style="color:red;">120</span>) / <span style="color:red;">10</span> + <span style="color:#808080;">"&#"</span> + rdq + <span style="color:#808080;">";"</span>;
    <span style="color:#000080;"><b><i>return</i></b></span> ft + <span style="color:#808080;">"&#"</span> + rsq + <span style="color:#808080;">";"</span> + Math.round((l - ft) * <span style="color:red;">120</span>) / <span style="color:red;">10</span> + <span style="color:#808080;">"&#"</span> + rdq + <span style="color:#808080;">";"</span>;
    

    }

    function calculate(bits, boards, fmt, fmt2) { var i, out = [], tmp, max = 0, num = 0;

    tmp = bits;
    bits = [];
    <span style="color:#000080;"><b><i>for</i></b></span> (i = <span style="color:red;">0</span>; i < tmp.options.length; bits.push(parseFloat(tmp.options[i ++].value)));
    
    tmp = boards;
    boards = [];
    <span style="color:#000080;"><b><i>for</i></b></span> (i = <span style="color:red;">0</span>; i < tmp.options.length; ++ i) {
        boards.push(parseFloat(tmp.options[<i></i>i].value));
        <span style="color:#000080;"><b><i>if</i></b></span> (boards[<i></i>i] > boards[max]) max = boards[<i></i>i];
        out.push([]);
    }
    
    <span style="color:#000080;"><b><i>if</i></b></span> (boards.length > <span style="color:red;">0</span>) {
        <span style="color:#000080;"><b><i>while</i></b></span> (bits.length > <span style="color:red;">0</span>) {
            <span style="color:#000080;"><b><i>var</i></b></span> bit = bits.shift(), f = <span style="color:#000080;"><b><i>true</i></b></span>;
            <span style="color:#000080;"><b><i>while</i></b></span> (bit > boards[max]) {
                ++ num;
                out[max].push([<span style="color:#000080;"><b><i>false</i></b></span>]);
                <span style="color:#000080;"><b><i>if</i></b></span> (f) {
                    out[max][out[max].length - <span style="color:red;">1</span>].push(bit);
                    f = <span style="color:#000080;"><b><i>false</i></b></span>;
                }
                bit -= boards[max];
            }
            
            <span style="color:#000080;"><b><i>for</i></b></span> (<span style="color:#000080;"><b><i>var</i></b></span> i = <span style="color:red;">0</span>, f = <span style="color:#000080;"><b><i>false</i></b></span>; i < out.length; ++ i) {
                <span style="color:#000080;"><b><i>for</i></b></span> (<span style="color:#000080;"><b><i>var</i></b></span> j = <span style="color:red;">0</span>; j < out[<i></i>i].length; ++ j) {
                    <span style="color:#000080;"><b><i>if</i></b></span> (out[<i></i>i][j][out[<i></i>i][j].length - <span style="color:red;">1</span>] !== <span style="color:#000080;"><b><i>false</i></b></span>
                        && boards[<i></i>i] - out[<i></i>i][j][out[<i></i>i][j].length - <span style="color:red;">1</span>] >= bit) {
                            f = <span style="color:#000080;"><b><i>true</i></b></span>;
                            <span style="color:#000080;"><b><i>break</i></b></span>;
                    }
                }
                <span style="color:#000080;"><b><i>if</i></b></span> (f) <span style="color:#000080;"><b><i>break</i></b></span>;
            }
            
            <span style="color:#000080;"><b><i>if</i></b></span> (f) out[<i></i>i][j].push(out[<i></i>i][j][out[<i></i>i][j].length - <span style="color:red;">1</span>] + bit);
            <span style="color:#000080;"><b><i>else for</i></b></span> (<span style="color:#000080;"><b><i>var</i></b></span> i = <span style="color:red;">0</span>; i < boards.length; ++ i) <span style="color:#000080;"><b><i>if</i></b></span> (boards[<i></i>i] >= bit) {
                ++ num;
                out[<i></i>i].push([bit]);
                <span style="color:#000080;"><b><i>break</i></b></span>;
            }
        }
    }
    
    <span style="color:#000080;"><b><i>var</i></b></span> output = document.getElementById(<span style="color:#808080;">"output"</span>), dir = boards[<span style="color:red;">0</span>] > boards[boards.length - <span style="color:red;">1</span>];
    output.innerHTML = <span style="color:#808080;">""</span>;
    <span style="color:#000080;"><b><i>if</i></b></span> (num > <span style="color:red;">0</span>) {
        <span style="color:#000080;"><b><i>var</i></b></span> bf = <span style="color:red;">0</span>, scrap = <span style="color:red;">0</span>, i = (dir ? <span style="color:red;">0</span> : boards.length - <span style="color:red;">1</span>)
        <span style="color:#000080;"><b><i>for</i></b></span> (; i >= <span style="color:red;">0</span> && i < boards.length; i += (dir ? <span style="color:red;">1</span> : -<span style="color:red;">1</span>)) <span style="color:#000080;"><b><i>if</i></b></span> (out[<i></i>i].length > <span style="color:red;">0</span>) {
            <span style="color:#000080;"><b><i>var</i></b></span> splice = <span style="color:#808080;">""</span>, result = <span style="color:#808080;">""</span>, num = <span style="color:red;">0</span>;
            <span style="color:#000080;"><b><i>for</i></b></span> (<span style="color:#000080;"><b><i>var</i></b></span> j = <span style="color:red;">0</span>; j < out[<i></i>i].length; ++ j) {
                <span style="color:#000080;"><b><i>if</i></b></span> (out[<i></i>i][j][<span style="color:red;">0</span>] == <span style="color:#000080;"><b><i>false</i></b></span>) {
                    ++ num;
                    <span style="color:#000080;"><b><i>if</i></b></span> (out[<i></i>i][j].length > <span style="color:red;">1</span>) splice += <span style="color:#808080;">"Make "</span> + formatFtIn(out[<i></i>i][j][<span style="color:red;">1</span>], fmt)
                        + <span style="color:#808080;">" by splicing together "</span> + (Math.floor(out[<i></i>i][j][<span style="color:red;">1</span>] / boards[<i></i>i]) > <span style="color:red;">1</span>
                            ? Math.floor(out[<i></i>i][j][<span style="color:red;">1</span>] / boards[<i></i>i]) + <span style="color:#808080;">" boards"</span> : <span style="color:#808080;">"a board"</span>)
                        + <span style="color:#808080;">" and a "</span> + formatFtIn(out[<i></i>i][j][<span style="color:red;">1</span>] % boards[<i></i>i], fmt) + <span style="color:#808080;">" piece<br/>"</span>;
                } <span style="color:#000080;"><b><i>else if</i></b></span> (dir && i < boards.length - <span style="color:red;">1</span>
                    && out[<i></i>i][j][out[<i></i>i][j].length - <span style="color:red;">1</span>] < boards[i + <span style="color:red;">1</span>]) {
                        out[i + <span style="color:red;">1</span>].push(out[<i></i>i][j]);
                } <span style="color:#000080;"><b><i>else if</i></b></span> (!dir && i > <span style="color:red;">0</span> && out[<i></i>i][j][out[<i></i>i][j].length + <span style="color:red;">1</span>] < boards[i - <span style="color:red;">1</span>]) {
                    out[i - <span style="color:red;">1</span>].push(out[<i></i>i][j]);
                } <span style="color:#000080;"><b><i>else</i></b></span> {
                    ++ num;
                    <span style="color:#000080;"><b><i>var</i></b></span> cuts = [], pieces = [];
                    <span style="color:#000080;"><b><i>for</i></b></span> (<span style="color:#000080;"><b><i>var</i></b></span> k = <span style="color:red;">0</span>; k < out[<i></i>i][j].length; ++ k) {
                        <span style="color:#000080;"><b><i>if</i></b></span> (out[<i></i>i][j][k] < boards[<i></i>i]) cuts.push(formatFtIn(out[<i></i>i][j][k], fmt));
                        <span style="color:#000080;"><b><i>if</i></b></span> (k > <span style="color:red;">0</span>) pieces.push(formatFtIn(out[<i></i>i][j][k] - out[<i></i>i][j][k - <span style="color:red;">1</span>], fmt));
                        <span style="color:#000080;"><b><i>else</i></b></span> pieces.push(cuts[cuts.length - <span style="color:red;">1</span>])
                    }
                    
                    <span style="color:#000080;"><b><i>if</i></b></span> (cuts.length == <span style="color:red;">2</span>) cuts = cuts.join(<span style="color:#808080;">" and "</span>);
                    <span style="color:#000080;"><b><i>else</i></b></span> {
                        <span style="color:#000080;"><b><i>if</i></b></span> (cuts.length > <span style="color:red;">1</span>) cuts[cuts.length - <span style="color:red;">1</span>] = <span style="color:#808080;">"and "</span> + cuts[cuts.length - <span style="color:red;">1</span>];
                        cuts = cuts.join(<span style="color:#808080;">", "</span>);
                    }
                    <span style="color:#000080;"><b><i>if</i></b></span> (pieces.length == <span style="color:red;">2</span>) pieces = pieces.join(<span style="color:#808080;">" and "</span>) + <span style="color:#808080;">" pieces"</span>;
                    <span style="color:#000080;"><b><i>else</i></b></span> {
                        <span style="color:#000080;"><b><i>if</i></b></span> (pieces.length > <span style="color:red;">1</span>) {
                            pieces[pieces.length - <span style="color:red;">1</span>] = <span style="color:#808080;">"and "</span> + pieces[pieces.length - <span style="color:red;">1</span>] + <span style="color:#808080;">" pieces"</span>;
                            pieces = pieces.join(<span style="color:#808080;">", "</span>);
                        } <span style="color:#000080;"><b><i>else</i></b></span> pieces = <span style="color:#808080;">"a "</span> + pieces.join() + <span style="color:#808080;">" piece"</span>;
                    }
                    result += <span style="color:#808080;">"Cut at "</span> + cuts
                        + <span style="color:#808080;">" to make "</span> + pieces
                        + (out[<i></i>i][j][out[<i></i>i][j].length - <span style="color:red;">1</span>] < boards[<i></i>i] ? <span style="color:#808080;">" and a "</span>
                            + formatFtIn(boards[<i></i>i] - out[<i></i>i][j][out[<i></i>i][j].length - <span style="color:red;">1</span>], fmt)
                            + <span style="color:#808080;">" scrap"</span> : <span style="color:#808080;">""</span>) + <span style="color:#808080;">"<br/>"</span>;
                    scrap += boards[<i></i>i] - out[<i></i>i][j][out[<i></i>i][j].length - <span style="color:red;">1</span>];
                }
            }
            <span style="color:#000080;"><b><i>if</i></b></span> (num > <span style="color:red;">0</span>)
                output.innerHTML += <span style="color:#808080;">"<br/>Number of "</span> + formatFtIn(boards[<i></i>i], fmt2)
                    + <span style="color:#808080;">" boards required: "</span> + num + <span style="color:#808080;">"<br/><br/>"</span> + result + splice;
            bf += boards[<i></i>i] * num;
        }
        output.innerHTML += <span style="color:#808080;">"<br/>"</span> + formatFtIn(bf, fmt2) + <span style="color:#808080;">" of trim required<br/>"</span>
            + formatFtIn(scrap, fmt) + <span style="color:#808080;">" of scrap ("</span> + Math.round(scrap / bf * <span style="color:red;">1000</span>) / <span style="color:red;">10</span> + <span style="color:#808080;">"%)"</span>;
    }
    

    } </script>

    </head>

    <body onload="var f=document.forms[0];addLength(f.bits,f.bitslist.value.split(','),null,'ft'); addLength(f.lengths,f.lengthslist.value.split(','),f.sort.value,'ft'); changeFmt(f.bits,null,f.fmt,f.fmt.value>''?f.fmt.value:'in',f.fmtbtn); changeFmt(f.lengths,f.sort.value,f.fmt2,f.fmt2.value>''?f.fmt2.value:'ftin',f.fmt2btn); calculate(f.bits,f.lengths,f.fmt.value,f.fmt2.value);">

    <form>
    Req<script</span> type="text/javascript">document.write("&#", rsq, ";");</script>d Lengths:
    <input</span> type="hidden" name="bitslist" value=""/> <select</span> name="bits" size="5" multiple onkeydown="return selectAll(this,event);" onchange="for(var i=s=0;i<this.options.length;s|=this.options[i++].selected); this.form.del.disabled=!s;this.form.bitslist.value=list(this); calculate(this.form.bits,this.form.lengths,this.form.fmt.value,this.form.fmt2.value);"</b>> </select> <input</span> type="button" name="add" value="+" class="btn" onclick="addLength(this.form.bits,null,null,this.form.fmt.value);"/>
    <input</span> type="button" name="del" value="-" class="btn" onclick="delLength(this.form.bits);"/>
    <input</span> type="hidden" name="fmt"/><input</span> type="button" name="fmtbtn" class="btn" value=" " onclick="changeFmt(this.form.bits,null,this.form.fmt,null,this);"/>
       
    Board Lengths:
    <input</span> type="hidden" name="lengthslist" value="16,8"/> <select</span> name="lengths" size="5" multiple onkeydown="return selectAll(this,event);" onchange="for(var i=s=0;i<this.options.length;s|=this.options[i++].selected); this.form.del2.disabled=!s;this.form.lengthslist.value=list(this); calculate(this.form.bits,this.form.lengths,this.form.fmt.value,this.form.fmt2.value);"</b>> </select> <input</span> type="button" name="add2" value="+" class="btn" onclick="addLength(this.form.lengths,null,this.form.sort.value,this.form.fmt2.value);"/>
    <input</span> type="button" name="del2" value="-" class="btn" onclick="delLength(this.form.lengths);"/>
    <input</span> type="hidden" name="fmt2"/><input</span> type="button" name="fmt2btn" class="btn" value=" " onclick="changeFmt(this.form.lengths,this.form.sort.value,this.form.fmt2,null,this);"/>
    <input</span> type="hidden" name="sort" value="desc"/><input</span> type="button" name="rev" value="%" class="btn" onclick="reverse(this.form.lengths,this.form.sort,this.form.fmt2.value);"/>
    <p</span> id="output">

    </form> </body> </html>
    Example output:
  • Axel (unregistered)

    I can't believe Alex's ridiculous "longer boards are cheaper by the foot" statement is still on page 1. As others have pointed out, it's exactly the opposite. Longer boards are rarer and thus more expensive, by any metric. Sheesh, ever even set foot in a real lumber yard?

Leave a comment on “Avoiding the Splice”

Log In or post as a guest

Replying to comment #:

« Return to Article