7
\$\begingroup\$

The goal is to display a bézier curve in any classical browser (except maybe old IE versions, because... are they really browsers?) client side.

Rules:

  • You must provide one or more files (or their content). These files must contain all the code.
  • The code must work offline, no imports from a remote source. No installation should be needed on the computer (except a vanilla browser).
  • This is a Javascript contest. A very simple way would be to use SVG files that can be read with browsers. Don't!
  • No need to pass the W3C validator test, you can omit the doctype and the surrounding tags (html, head, body, ...)
  • The input can take any form: GET, POST, written in the code, etc.
  • The quadratic bézier must be implemented. (2nd degree bézier: starting point, ending point and 1 intermediate point). It means 3 points (= 6 coordinates) must be given in whatever format you prefer (XML, JSON, ...).
  • If you don't know what a bézier curve is, you should! Check on Wikipedia to know about it.

Scoring:

  • In his great kindness, OP offers you 10'000 point just by posting a valid answer.
  • The sum of the sizes of the files in bytes will give negative points. 1 byte = -1 point*.
  • Implementing the cubic bézier curve instead of the quadratic multiplies your points by 120%.
  • Implementing n-th degree bézier curve instead of the quadratic or cubic double your points. (Which means implementing both the cubic and the n-th degree is useless, pointless and very dumb).
  • Implementing filled bézier (with inner color) will double your points again.
  • There is no limit in the number of files or libraries you use, just remember adding jQuery, for example, already gives you about -26'000 points.

The contestant with the highest score wins. (duh!)

*A 10'000 characters answer gives you 0 points.

\$\endgroup\$
  • 1
    \$\begingroup\$ I'm not sure I get the question. Is it allowed to generate some SVG ? Is it allowed to use canvas (in which a native bezier api is available) ? \$\endgroup\$ – Denys Séguret Apr 19 '13 at 13:15
  • \$\begingroup\$ Good question. Of course, you can't use any built-in bézier whatsoever (SVG, canvas bézier, ...) you must implement the bézier. After that, you can use canvas to display it, if you want. \$\endgroup\$ – SteeveDroz Apr 19 '13 at 14:01
7
\$\begingroup\$

39240 points (190 chars)

<script>P='<svg><path stroke=red d=M'+X[n]+','+Y[n]
for(t=N=99;t--;P+='L'+x+','+y)for(x=y=i=0,z=Math.pow(1-t/N,n);i<=n;z*=t/(N-t)*(n-i)/++i)x+=z*X[i],y+=z*Y[i]
document.write(P+'>')</script>

Input is supplied in three variables: X and Y hold the coordinates, and n holds the degree of the polynomial. If more coordinates are supplied than are needed, the surplus ones are ignored. The line is stroked in red and filled in black.

This example draws substantially from the existing ones, but with an improvement of 40% in character count over the current best character count I think it's worth a separate answer.

Demo: http://jsfiddle.net/MBCGJ/7/

This is stretching the flexibility of the rules too far IMO, but three characters can be saved by making N a "fineness" parameter which must be initialised along with the input coordinates and degree.

Also, Chrome at least doesn't care about closing the path element at all, which allows saving the 4 chars +'>', so if that works cross-platform and the fineness parameter abuse is allowed this can go down to 183 chars.


The preferred way of rendering Bézier curves is, of course, adaptive subdivision using the de Casteljau algorithm. Unfortunately this uses more characters because it's necessary to do a test to decide whether to subdivide and then to do a moderate amount of work for the subdivision itself. Currently I'm at 371 chars, although I think this could be reduced with an explicit stack.

<script>P='<svg><path stroke=red d=M'+X[0]+','+Y[0]
function Z(A,B,E,F){function d(p,q){return(t=A[p]-A[q])*t+(t=B[p]-B[q])*t}for(e=i=0;++i<n;e|=b+c-d(n,0)+Math.sqrt(3.9*b*c)>0)b=d(i,0),c=d(n,i)
for(E=[],F=[i=-1];e?i++<n||Z(E,F)|Z(A,B):0;)for(E[i]=A[0],F[i]=B[j=0];j<n-i;B[j]=(B[j]+B[++j])/2)A[j]=(A[j]+A[j+1])/2
P+='L'+A[n]+','+B[n]}Z(X,Y)
document.write(P+'>')</script>

The figure 3.9 in the middle there is the quality parameter, which must be less than 4 to avoid infinite recursion. The closer it gets to 4, the better the quality.

http://jsfiddle.net/FrJLn/2/

\$\endgroup\$
6
\$\begingroup\$

38,716 points (321 chars)

<canvas id="c"></canvas><script>function c(n,k){return k<1||c(n-1,k-1)*n/k}function b(p,f){d=document.getElementById('c').getContext('2d')
for(t=0;t<1.009;t+=0.01){n=p.length-1
x=y=i=0
e=Math.pow
for(;i<=n;z=c(n,i)*e(1-t,n-i)*e(t,i),x+=z*p[i][0],y+=z*p[i][1],i++);d.lineTo(x,y)}d.stroke()
d.fillStyle=f
d.fill()}</script>

Given an array of points and the desired fill colour, the b function draws an n-th degree bézier curve onto the canvas. It uses the binomial theorem to calculate the location of 101 different points along the curve, and then joins them together with straight lines.

Example

\$\endgroup\$
  • \$\begingroup\$ ah man function c(n,k){return k<1||c(n-1,k-1)*n/k} is clever. Annoyed with myself for not thinking of that \$\endgroup\$ – Griffin Apr 19 '13 at 14:19
  • \$\begingroup\$ I actually copied the idea from one of my previous golfs. I was well into writing it the factorial way before I realised I had done it before :) \$\endgroup\$ – grc Apr 19 '13 at 14:42
  • \$\begingroup\$ Don't reinvent the wheel, even in golfing! :) \$\endgroup\$ – user7486 Apr 19 '13 at 15:01
  • \$\begingroup\$ Why make it a function? You can save 32 chars (at least) by inlining it. \$\endgroup\$ – Peter Taylor Apr 19 '13 at 21:50
  • \$\begingroup\$ I don't know how to save that many chars without copying other parts of your answer. I'll just leave mine as it is since you've already done it better than I'd be able to (and I've had enough of bézier curves) :P \$\endgroup\$ – grc Apr 20 '13 at 3:54
4
\$\begingroup\$

716 Chars - 37136 points

<script>function f(n){return n<1||n*f(n-1)}function c(n,k){return f(n)/(f(k)*f(n-k))}U=")*Math.pow(",G="createElementNS",$="setAttribute",d=document,w="appendChild",y=["",j=""];window.onload=function(){v=d.getElementById("s");q=prompt().split(" ");e=q.length-1;for(z=0;z<=e;z++){q[z]=q[z].split(",");p=q[z]={x:q[z][0],y:q[z][1]};_=d[G]("http://www.w3.org/2000/svg","circle");_[$]("cx",p.x);_[$]("cy",p.y);_[$]("r",5);_[$]("fill","red");v[w](_);for(u=0;u<2;u++)y[u]+="(c("+e+","+z+U+"1-t,"+(e-z)+U+"t,"+z+")*q["+z+"]."+"xy"[u]+")"+(z^e?"+":"")}for(t=0;t<=1.01;t+=0.01)j+=eval(y[0])+","+eval(y[1])+" ";l=d[G]("http://www.w3.org/2000/svg","polyline");l[$]("points",j);l[$]("stroke","red");v[w](l)}</script><svg id="s"/>

Try it in this JFiddle

You will be prompted to provide your points in the following format:

x1,y1 x2,y2 x3,y3 x4,y4 x5,y5

Check list

  • n-degree handled
  • beziers are filled
  • no external libraries
  • plots control points

An example output of a 34th degree curve (non filled):

enter image description here

441 Chars - 38236 points

This doesn't plot the control points and it, shamelessly, uses grc's n-choose-k function. so it's not my real submission

<script>function c(n,k){return k<1||c(n-1,k-1)*n/k}V=Math.pow,$="setAttribute",d=document,window.onload=function(){A=prompt().split(" ");e=A.length-1,j="";for(t=0;t<=1.01;t+=0.01){x=y=0;for(z=0;z<=e;z++)p=A[z].split(","),f=c(e,z)*V(1-t,e-z)*V(t,z),x+=f*p[0],y+=f*p[1];j+=x+","+y+" "}l=d.createElementNS("http://www.w3.org/2000/svg","polyline");l[$]("points",j);l[$]("stroke","red");d.getElementById("s").appendChild(l)}</script><svg id="s"/>
\$\endgroup\$
1
\$\begingroup\$

186 bytes (39256 points?)

<script>function m(a,j,l){return l?m(a,j,--l)*(1-i)+m(a,j+1,l)*i:a[j]}P='<svg><path stroke=red d='
for(i=0;i<=1;i+=1/64)P+='ML'[i&&1]+m(X,0,n)+','+m(Y,0,n)
document.write(P+'>')</script>

Usage is exactly the same as Peter Taylor's solution. Also credits to Peter for the svg path approach (I wanted to use a canvas but it turns out to be longer). I would normally delete the "stroke=red" part (to get 175 bytes) but I kept it for having a fair comparison.

\$\endgroup\$
  • \$\begingroup\$ I think the answers so far assume that stroke=red is necessary to qualify for the filled curve bonus. \$\endgroup\$ – Peter Taylor May 1 '13 at 7:32
  • \$\begingroup\$ @PeterTaylor I assume it's not necessary - it's already filled with black \$\endgroup\$ – aditsu May 1 '13 at 7:36
  • \$\begingroup\$ The question is whether "(with inner color)" implies that the inner colour must be different to the stroke colour. Although I should test whether fill=red works, since it's definitely shorter... \$\endgroup\$ – Peter Taylor May 1 '13 at 8:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.