0

I have a flat image that I want to transform so it looks like a projection on an elliptic cylinder.

I've found the great script from Blindman67 for a regular cylinder at https://stackoverflow.com/a/40999097/4273683

However I don't understand, how to change the script to get an elliptic result. Any idea?

Thanks a lot for help.

var createImage=function(w,h){var i=document.createElement("canvas");i.width=w;i.height=h;i.ctx=i.getContext("2d");return i;}

var canvas = createImage(400,400);
var ctx = canvas.ctx;
document.body.appendChild(canvas)
ctx.clearRect(0,0,500,500)
var image = createImage(400,200);
image.ctx.font = "60px arial";
image.ctx.textAlign = "center";
image.ctx.fillStyle = "#7F5";
image.ctx.fillRect(0,0,image.width,image.height)
image.ctx.fillStyle = "white";
image.ctx.fillText("Wrap around",200,60)
image.ctx.fillText("Some images",200,140)


function draw(ang,tilt, perspective){
    var step = 1/(Math.max(image.width,400));
    for(var i = 0; i < 1; i += step){
        var a = i * Math.PI;
        var a1 = (i+ step*2) * Math.PI ;
        var ix = i * image.width*1.2;
        var iw = step * image.width*1.2;
        a += ang * Math.PI * 2;
        a1 += ang * Math.PI * 2;
        a = Math.PI -a;
        a1 = Math.PI -a1;
        var x = canvas.width * 0.5;
        var y = canvas.height * 0.1;
        
        var x1 = x + Math.cos(a1) * 110;
        var y1 = y + Math.sin(a) * tilt;
        x += Math.cos(a) * 110;
        y += Math.sin(a) * tilt;
        var s = Math.sin(a);
        var s1 = Math.sin(a1);
        if(s > 0 || s1 > 0){
            ctx.drawImage(image,ix,0,iw,image.height,x1,y- s * perspective*0.5,x-x1,200 + s * perspective)
        }
        
        
    }
}
var w = canvas.width;
var h = canvas.height;




// main update function
function update(timer){
    ctx.setTransform(1,0,0,1,0,0); // reset transform
    ctx.globalAlpha = 1;           // reset alpha
    ctx.fillStyle = "black"
    ctx.fillRect(0,0,w,h);
    draw(timer / 2000, 40,30)
    requestAnimationFrame(update);
}
requestAnimationFrame(update);
kirschkern
  • 1,197
  • 10
  • 27
  • Can you elaborate? Given that there's basic perspective distortion here, the result is strictly speaking already an ellipse. How else exactly do you want this to work? –  Nov 27 '17 at 13:59
  • The function is limited to distortion in one axis,(at any angle). It draws many strips scaled and translated to look like the image is mapped to a cylinder. In the example the vertical lines will always be parallel. Mapping to another surface is possible with a second pass but the possible surfaces are very limited. You will have to describe the type of surface (or provide and example image) for us to know if what you want can be done using the same method, or needs another solution. – Blindman67 Nov 27 '17 at 15:48
  • 1
    Why not consider using WebGL/three.js for 3D shapes ([example](https://jsfiddle.net/epistemex/cae6h6z8/))? If this is an option I can add an answer for that. –  Nov 28 '17 at 02:49
  • The final result should be a shirt preview used in a knitting product editor. The original image contains 4 parts (front, back, arm left/right) and should be shown in a preview. I was able to get pretty far but would like to have a "flat belly" in the resulting image. This is what I have so far: http://klotzen-statt-kleckern.de/customers/wildemasche/designer/js/livepreview/test.php (Klick the 4-part images to generate the preview). The cylinder function is currently used for the front and the (half) arms. I've thought about using WebGL but have zero experience in this field. Thanks a lot! – kirschkern Nov 28 '17 at 15:01
  • @K3N Great demo! Seems to be understandable. When using WebGL I will probably use a different approach and map the image onto a 3d model, right? – kirschkern Nov 28 '17 at 15:12
  • @kirschkern correct, you would prepare the model in fex. Blender (free), Lightwave, Maya or any other 3D package (or buy a premade model). Triangulate and define a UV map which is a 2D map representing the parts of the model and what you use to paint "on" in canvas. Then export that model as Wavefront OBJ, Collada or something that your software can read (I recommend using a WebGL wrapper library such as the shown three.js - using WebGL directly can reduce the total code base, but it's laborious as you need to setup all the nitty gritty stuff manually). From there it's pretty straight forward. –  Nov 28 '17 at 19:52
  • @kirschkern the 3D approach also allow you to use things such as bump or normal maps (the latter also allow you to use low-res models and still have them look good) to make the model look more realistic when combined with lights. And of course, almost without any effort you can allow the user to rotate and view the model from different angles and so forth (in the case of three.js). –  Nov 28 '17 at 19:54
  • @K3N Sounds good and like the way to go in the next round. Thanks for the explanation und helpfull starting points. – kirschkern Nov 29 '17 at 09:52

0 Answers0