- Feature Articles
- CodeSOD
- Error'd
- Forums
-
Other Articles
- Random Article
- Other Series
- Alex's Soapbox
- Announcements
- Best of…
- Best of Email
- Best of the Sidebar
- Bring Your Own Code
- Coded Smorgasbord
- Mandatory Fun Day
- Off Topic
- Representative Line
- News Roundup
- Editor's Soapbox
- Software on the Rocks
- Souvenir Potpourri
- Sponsor Post
- Tales from the Interview
- The Daily WTF: Live
- Virtudyne
Admin
The earth is flat?
Admin
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.
Admin
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.
Admin
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:
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.
Admin
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.
Admin
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.).
Admin
...and by "non-irrational" I was of course referring to "integer"...
Admin
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).
Admin
Time results for my genetic algorithm Post 296599
Admin
Admin
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?
Admin
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.
Admin
Actually, the idea dates back to at least the Romans.
Admin
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
Admin
And mud-hut-dweller-unfriendly.
Admin
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?
Admin
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 :(.
Admin
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 ;-)
Admin
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? >
Admin
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)
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)
end
--set a cached result
function setStoredResults(room,cover,result)
end
--applies a cover to the room --creates a new 'room' for testing further plans
function createTestRoom(room,cover)
end
--test if a cover plan has already coverd all the doors
function isAllCovered(room, cover)
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)
end
function makePoposalInTestRoom(testRoom,cover,len,startEdge)
end
function makeProposals(room, cover, len)
end
function evalProposal(room, cover, proposal)
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)
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
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()
end
main()
Admin
Admin
/* 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; }
} return boards; }
Admin
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.
Admin
You people ruined en.wikipedia with your POV boxes
Admin
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")
Admin
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")
Admin
Here's the math that took me all of a minute to think out: floor(lengthofboard/sumofneededunsplicedlengthperobject)*(number_of_these_objects)
Admin
<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%5Dm}o.innerHTML+="
"+g(bf,Z)+" of trim required
"+g(s,z)+" of scrap ("+Math.round(s/bf1000)/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>
<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);"/>
<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)"/>
"}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/bf1000)/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>
<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);"/>
<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)"/>
<!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>
Req<script</span> type="text/javascript">document.write("&#", rsq, ";");</script>d Lengths:
Board Lengths:
<p</span> id="output">
</form>
</body>
</html>
Example output:<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(","); }
}
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");
}
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";
}
function calculate(bits, boards, fmt, fmt2) { var i, out = [], tmp, max = 0, num = 0;
} </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><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);"/>
<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);"/>
Admin
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?