#version 3.7; global_settings { assumed_gamma 1.0 } //------------------------------------------ // SDL for making a Bezier spline - given 4 data points, compute control points p1 and p2 // Bill Walker - 2016 // Adapted code to POV-Ray from : // https://www.particleincell.com/2012/bezier-splines/ // https://www.particleincell.com/wp-content/uploads/2012/06/circles.svg // replace var with #declare or #local, function with #macro, edit #for() #end syntax, array[] syntax //------------------------------------------ #include "colors.inc" #include "debug.inc" Set_Debug (true) #include "math.inc" light_source { <0, 50, -50> color rgb <1, 1, 1>} #declare Camera_Front = camera { location <0, 90, -1> //right x*image_width/image_height look_at <0, 0, 0>} camera {Camera_Front} // bezier-spline.js // computes cubic bezier coefficients to generate a smooth // line through specified points. couples with SVG graphics // for interactive processing. // For more info see: // http://www.particleincell.com/2012/bezier-splines/ // Lubos Brieda, Particle In Cell Consulting LLC, 2012 // you may freely use this algorithm in your codes however where feasible // please include a link/reference to the source article //#declare svg=document.documentElement //svg object #declare S = array[4]; //splines #declare V = array[4]; //vertices //#declare C //current object //#declare x0,y0 //svg offset #declare K = array[4]; #macro init() //################################################################################## //saves elements as global variables //create splines //#declare S[0] = createPath("blue"); //#declare S[1] = createPath("red"); //#declare S[2] = createPath("green"); //#declare S[3] = createPath("brown"); //create control points #declare V[0] = <60,60,0>; createKnot(V[0]) #declare V[1] = <220,300,0>; createKnot(V[1]) #declare V[2] = <420,300,0>; createKnot(V[2]) #declare V[3] = <700,240,0>; createKnot(V[3]) updateSplines() #end // end macro init //################################################################################## #macro createPath(Color, Width) //################################################################################## //creates and adds an SVG path without defining the nodes //width = (typeof width == 'undefined' ? "8" : width); //#declare P=document.createElementNS("http://www.w3.org/2000/svg","path") //P.setAttributeNS(null,"fill","none") //P.setAttributeNS(null,"stroke",color) //P.setAttributeNS(null,"stroke-width",width) //svg.appendChild(P) //return P #end //################################################################################## #macro createKnot(V) //################################################################################## //creates and adds an SVG circle to represent knots sphere { V 22 pigment {Yellow}} //#declare C=document.createElementNS("http://www.w3.org/2000/svg","circle") //C.setAttributeNS(null,"r",22) //C.setAttributeNS(null,"cx",x) //C.setAttributeNS(null,"cy",y) //C.setAttributeNS(null,"fill","gold") //C.setAttributeNS(null,"stroke","black") //C.setAttributeNS(null,"stroke-width","6") //C.setAttributeNS(null,"onmousedown","startMove(evt)") //svg.appendChild(C) //return C #end // end macro createKnot //################################################################################## // Maybe set up java mouse movement as clock-linked animation //from http://www.w3.org/Graphics/SVG/IG/resources/svgprimer.html /* #macro startMove(evt) SVG positions are relative to the element but mouse positions are relative to the window, get offset x0 = getOffset(svg).left; y0 = getOffset(svg).top; C=evt.target svg.setAttribute("onmousemove","move(evt)") svg.setAttribute("onmouseup","drop()") #end // end macro startMove //called on mouse move, updates dragged circle and recomputes splines #macro move(evt) x = evt.clientX-x0; y = evt.clientY-y0; move the current handle C.setAttributeNS(null,"cx",x) C.setAttributeNS(null,"cy",y) updateSplines(); #end // end macro move //called on mouse up event #macro drop() svg = document.getElementsByTagName('svg')[0]; svg.setAttributeNS(null, "onmousemove",null) #end // end macro drop */ #macro updateSplines() //################################################################################## //computes spline control points //grab (x,y) coordinates of the control points #local X = array[4]; #local Y = array[4]; #for (i, 0, 3) //for (i=0;i<4;i++) //use parseInt to convert string to int #local X[i] = V[i]; //parseInt(V[i].getAttributeNS(null,"cx")) #local Y[i] = V[i]; //parseInt(V[i].getAttributeNS(null,"cy")) #end //computes control points p1 and p2 for x and y direction #local px = computeControlPoints(X); #local py = computeControlPoints(Y); //updates path settings, the browser will draw the new spline #for (i, 0, 2) //for (i=0;i<3;i++) //S[i].setAttributeNS(null,"d", path(x[i],y[i],px.p1[i],py.p1[i],px.p2[i],py.p2[i],x[i+1],y[i+1])); //#debug concat( "(Draw SVG path subroutine) i = ", str(i, 3, 1), "\n") #end #end // end macro updateSplines ^^^^ call path macro //################################################################################## #macro path(x1,y1,px1,py1,px2,py2,x2,y2) //################################################################################## //creates formated path string for SVG cubic path element //return "M "+x1+" "+y1+" C "+px1+" "+py1+" "+px2+" "+py2+" "+x2+" "+y2; #end // end macro paths //################################################################################## #macro computeControlPoints(K) //################################################################################## //computes control points given knots K, this is the brain of the operation #local p1 = array[4]; #local p2 = array[4]; #local n = dimension_size (K, 1) - 1; //K.length-1; //rhs vector #local a = array[n]; #local b = array[n]; #local c = array[n]; #local r = array[n]; //left most segment #local a[0] = 0; #local b[0] = 2; #local c[0] = 1; #local r[0] = K[0] + 2 * K[1]; //internal segments #for (i, 0, n-2) //(i = 1; i < n - 1; i++) #local a[i] = 1; #local b[i] = 4; #local c[i] = 1; #local r[i] = 4 * K[i] + 2 * K[i+1]; #end //right segment #local a[n-1] = 2; #local b[n-1] = 7; #local c[n-1] = 0; #local r[n-1] = 8 * K[n-1] + K[n]; //solves Ax=b with the Thomas algorithm (from Wikipedia) #for (i, 1, n-1) //#for (i = 1; i < n; i++) #local m = a[i]/b[i-1]; #local b[i] = b[i] - m * c[i - 1]; #local r[i] = r[i] - m*r[i-1]; #end #local p1[n-1] = r[n-1]/b[n-1]; //#local p1[n-1] = r[n-1]/b[n-1]; // {Be sure to include debug.inc file!} //#debug concat( "Defining p1. n-1 = ", str((n-1), 3, 1), "\n") #for (i, n-2, 0, -1) //#for (i = n - 2; i >= 0; --i) #local p1[i] = (r[i] - c[i] * p1[i+1]) / b[i]; //#debug concat( "Defining p1. i = ", str(i, 3, 1), " Defining p1. i+1 = ", str((i+1), 3, 1),"\n") #end //we have p1, now compute p2 #for (i, 0, n-2) //#for (i=0;i