How to apply transform to graphics in OpenFL - graphics

I'm converting a JavaScript library to Haxe. In this library, there is an animated effect constructed with many of shapes. So I used the OpenFL library to render shapes.
But now I have a technical problem with transformation.
Some of the shapes has the child shapes so it's transform should be applied to the child shapes too.
For example, please imagine shapeC is attached on shapeB and, shapeB and shapeD are also attached on shapeA. In this case, shapeB, shapeD should be transformed by both of transformA and their own transform and, shapeC also should by transformA, transformB and transformC.
To achieve this, is it a good solution to render the same level shapes in one graphic and apply the parent's transform to that graphic? (on above example, render shapeB and shapeD to one graphic and a apply transformA to that graphic)
I think it's not a good optimized solution to calculate the final transform from all parents transforms and apply that to all vertexes of that shape. Please tech me the best optimized solution for rendering.
Any suggestion will be welcome.
And if there is any confused things on this question, please pardon me and let me check.

You can use the Sprite class:
var parentShape = new Sprite ();
parentShape.graphics.beginFill (0xFF0000);
parentShape.graphics.drawRect (0, 0, 100, 100);
var childShape = new Sprite ();
childShape.graphics.beginFill (0x00FF00);
childShape.graphics.drawCircle (0, 0, 50);
childShape.x = 200;
childShape.y = 200;
parentShape.addChild (childShape);
addChild (parentShape);
Each shape will use its own canvas element, so if you create a lot of shapes, you may decide to flatten it into a single image when you are ready. This is possible using cacheAsBitmap or bitmapData.draw
parentShape.cacheAsBitmap = true;
...or
removeChild (parentShape);
var bitmapData = new BitmapData (Math.ceil (parentShape.width), Math.ceil (parentShape.height), true, 0);
bitmapData.draw (parentShape);
var bitmap = new Bitmap (bitmapData);
addChild (bitmap);

Related

Why isn't this mask working in Phaser?

I must be missing something...why isn't this working? Instead of clipping to the circle the entire 800x800 backdrop image is displayed...
var mask;
var img;
function preload() {
game.load.image('back', 'backdrop.jpg');
}
function create() {
img = game.add.image(game.world.centerX, game.world.centerY,'back').anchor.setTo(0.5);
mask = game.add.graphics(0, 0);
mask.beginFill(0xffffff);
mask.drawCircle(game.world.centerX, game.world.centerY, 600);
img.mask = mask;
}
jsfiddle here
Disclaimer: I have no formal experience in phaser.io
I was able to fix this in your fiddle by changing
img = game.add.image(game.world.centerX, game.world.centerY,'back').anchor.setTo(0.5);
to
img = game.add.image(0, 0, 'back');
JSFiddle Fork
I would assume that placing the image in the centerX,centerY position results in the mask being offset of the image. Hopefully someone with more experience than I could explain the specifics here, but I will research further and update my answer as I figure out the why to go along with the how.
Update
Okay so I've done some digging through the documentation. First, you want to use img = game.add.image(0, 0, 'back'); due to the fact that the x and y parameters in this case dictate the upper-left origin of the image, not the center. By using game.world.centerX and game.world.centerY you are trying to throw the background image to the center of the canvas even though the canvas is the same size as the image.
using .anchor.setTo(0.5) from what I can gather, is attempting to set the anchor point at which the image originates to the centerX position. However, when you remove this anchor, suddenly the mask works, even though it is not showing correctly (because the position of the background image is incorrect).
Theory - By anchoring the image, I believe that it is no longer possible to apply a mask to it. By all experimenting that I've done, having an anchor set on the background image prevents it from being masked, so the mask simply is added as a child to img and is placed as it's center, thus why you are seeing the white circle instead of the circle properly masking the image.
it appears I was a mistaken about the fluidity of the api in trying to chain that last function call... if I break it up:
img = game.add.image(game.world.centerX, game.world.centerY, 'back');
img.anchor.setTo(0.5);
it now works!
Fiddle Here

Can fabric.js parse raster graphic to "real" svg?

I have a question/problem with fabric.js - in my code the user can upload a picture, with filters he can convert it to a black/white image. When I export the picture with canvas.toSVG(); it exports a svg image, but it is no real vector graphic - it loses quality when scaling up.
function handleImage(e) {
var reader = new FileReader();
reader.onload = function (event) {
var img = new Image();
img.onload = function () {
var imgInstance = new fabric.Image(img, {
scaleX: 0.7,
scaleY: 0.7
})
canvas.add(imgInstance);
}
img.src = event.target.result;
} reader.readAsDataURL(e.target.files[0]);}
$('saveBtn').onclick = function() {
var filedata= canvas.toSVG(); // the SVG file is now in filedata
var locfile = new Blob([filedata], {type: "image/svg+xml;charset=utf-8"});
var locfilesrc = URL.createObjectURL(locfile);//mylocfile);
var dwn = document.getElementById('dwn');
dwn.innerHTML = "<a href=" + locfilesrc + " download='mysvg.svg'>Download</a>";}
What am I doing wrong?
There is no easy way to "parse" raster graphics to a vector image. Vector graphics include information for how to draw an image, while raster images only include the pixel data for how an image appears at a given size and resolution. That's enough for many purposes, but it means that while it's easy to go from vector to raster (just execute the instructions), it's not easy to go from raster to vector.
It is possible to "trace" the edges of a raster image to obtain vectors that can approximate the raster: in other words, a set of vector instructions that, for that particular resolution and depth, yields an image that is the same as the original raster (or something very like it). But there is no guarantee that these actually correspond in any way to the original vectors (if there are any original vectors at all). Usually there is no correspondence, in fact, unless your tracing algorithm is very specialized: for example, tracing images of a font to make a vector copy of that font. Because they don't correspond, there's no guarantee that the image will scale up the way you want it to: it'll scale, but things may enlarge in strange ways.
It is possible to implement tracing algorithms in JavaScript, by drawing the image into a <canvas> element, using getImageData() to grab the pixel information from that, and performing your operations on the pixel information. Doing this, though, is beyond the scope of this question.

Extruding multiple polygons with multiple holes and texturing the combined shape

This question is related to this question. The answer shows very nice way to extrude polygons that have holes (see the excellent live example). The main learning of the answer was that paths in three.js (r58) cannot have more than one moveTo command and it have to be in the start of the path, which means that path have to be broken by moveTos, so that moveTo start always a new path.
Extruding in three.js means that 2D paths are converted to 3D shapes using possible beveling. It is suitable for extruding texts to make 3D letters and words, but can be used also to extrude custom paths.
Now there arises two questions:
how is it possible to handle polygons that have multiple hole-polygons and multiple non-hole-polygons?
how is it possible to add a texture to generated shape as a whole?
I made an example of this as SVG in http://jsbin.com/oqomuj/1/edit:
The image is produced using this path:
<path d="
M57.11,271.77 L57.11,218.33 L41.99,218.63 L105.49,165.77 L138.41,193.18 L138.41,172.2 L152.53,172.2 L152.53,204.93 L168.99,218.63 L153.21,218.63 L153.21,271.77Z
M74.14,264.13 L105.49,264.13 L105.49,232.8 L74.14,232.8Z
M115.35,250.7 L135.96,250.7 L135.96,232.61 L115.35,232.61Z
M56.11,145.77 L56.11,92.33 L40.99,92.63 L104.49,39.77 L137.41,67.18 L137.41,46.2 L151.53,46.2 L151.53,78.93 L152.53,79.76 L155.55,77.23 L159.5,74.52 L168.65,69.81 L176.46,66.93 L188.04,64.16 L200.63,62.7 L213.65,62.7 L226.05,64.09 L234.83,66.06 L245.65,69.73 L252.87,73.27 L259.12,77.34 L262.63,80.33 L265.6,83.47 L268.01,86.76 L269.83,90.17 L271.08,93.68 L271.76,99.08 L271.04,104.64 L269.75,108.2 L267.87,111.63 L265.42,114.91 L262.44,118.01 L258.95,120.92 L255.02,123.63 L245.86,128.34 L238.06,131.22 L226.48,133.99 L213.88,135.44 L200.63,135.44 L188.04,133.99 L176.46,131.22 L168.65,128.34 L159.5,123.63 L155.55,120.92 L152.21,118.12 L152.21,145.77Z
M73.14,138.13 L104.49,138.13 L104.49,106.8 L73.14,106.8Z
M114.35,124.7 L134.96,124.7 L134.96,106.61 L114.35,106.61Z
M207.26,117.33 L210.57,117.26 L216.87,116.53 L222.66,115.15 L227.8,113.18 L233.11,110 L236.34,106.99 L238.51,103.64 L239.42,100.48 L239.42,97.67 L238.51,94.51 L236.34,91.16 L233.11,88.15 L227.8,84.97 L222.66,83 L216.87,81.62 L210.57,80.89 L203.94,80.89 L197.65,81.62 L191.86,83 L186.71,84.97 L181.41,88.15 L178.18,91.16 L176.01,94.51 L175.1,97.67 L175.1,100.48 L176.01,103.64 L178.18,106.99 L181.41,110 L186.71,113.18 L191.86,115.15 L197.65,116.53 L203.94,117.26Z
"></path>
and this path converted to individual arrays of vertices:
var lower_house_material = [{x:57.11,y:271.77},{x:57.11,y:218.33},{x:41.99,y:218.63},{x:105.49,y:165.77},{x:138.42,y:193.18},{x:138.42,y:172.2},{x:152.53,y:172.2},{x:152.53,y:204.93},{x:168.99,y:218.63},{x:153.21,y:218.63},{x:153.21,y:271.77}];
var lower_house_hole_1 = [{x:74.14,y:264.13},{x:105.49,y:264.13},{x:105.49,y:232.8},{x:74.14,y:232.8}];
var lower_house_hole_2 = [{x:115.35,y:250.7},{x:135.96,y:250.7},{x:135.96,y:232.61},{x:115.35,y:232.61}];
var upper_house_material = [{x:56.11,y:145.77},{x:56.11,y:92.33},{x:40.99,y:92.63},{x:104.49,y:39.77},{x:137.42,y:67.18},{x:137.42,y:46.2},{x:151.53,y:46.2},{x:151.53,y:78.93},{x:152.53,y:79.76},{x:155.55,y:77.23},{x:159.5,y:74.52},{x:168.65,y:69.81},{x:176.46,y:66.93},{x:188.04,y:64.16},{x:200.63,y:62.7},{x:213.65,y:62.7},{x:226.05,y:64.1},{x:234.83,y:66.06},{x:245.65,y:69.73},{x:252.87,y:73.27},{x:259.12,y:77.35},{x:262.63,y:80.33},{x:265.6,y:83.47},{x:268.01,y:86.76},{x:269.84,y:90.17},{x:271.08,y:93.68},{x:271.76,y:99.08},{x:271.04,y:104.64},{x:269.75,y:108.2},{x:267.87,y:111.63},{x:265.42,y:114.91},{x:262.44,y:118.01},{x:258.96,y:120.92},{x:255.02,y:123.63},{x:245.86,y:128.34},{x:238.06,y:131.22},{x:226.48,y:133.99},{x:213.88,y:135.45},{x:200.63,y:135.45},{x:188.04,y:133.99},{x:176.46,y:131.22},{x:168.65,y:128.34},{x:159.5,y:123.63},{x:155.55,y:120.92},{x:152.21,y:118.12},{x:152.21,y:145.77}];
var upper_house_hole_1 = [{x:73.14,y:138.13},{x:104.49,y:138.13},{x:104.49,y:106.8},{x:73.14,y:106.8}];
var upper_house_hole_2 = [{x:114.35,y:124.7},{x:134.96,y:124.7},{x:134.96,y:106.61},{x:114.35,y:106.61}];
var upper_house_hole_3 = [{x:207.26,y:117.33},{x:210.57,y:117.26},{x:216.87,y:116.53},{x:222.66,y:115.15},{x:227.8,y:113.18},{x:233.11,y:110},{x:236.34,y:106.99},{x:238.51,y:103.64},{x:239.42,y:100.48},{x:239.42,y:97.67},{x:238.51,y:94.51},{x:236.34,y:91.16},{x:233.11,y:88.15},{x:227.8,y:84.97},{x:222.66,y:83},{x:216.87,y:81.62},{x:210.57,y:80.89},{x:203.94,y:80.89},{x:197.65,y:81.62},{x:191.86,y:83},{x:186.71,y:84.97},{x:181.41,y:88.15},{x:178.18,y:91.16},{x:176.01,y:94.51},{x:175.1,y:97.67},{x:175.1,y:100.48},{x:176.01,y:103.64},{x:178.18,y:106.99},{x:181.41,y:110},{x:186.71,y:113.18},{x:191.86,y:115.15},{x:197.65,y:116.53},{x:203.94,y:117.26}];
The question is, how this like structure can be converted to 3D object in three.js so that it can be extruded using THREE.ExtrudeGeometry( shape, extrusionSettings ) and after that textured as a whole?
I can examine the path data to know what hole belongs to what polygon and handle all as separate shapes, but because I want to use one texture image across all the shapes, I think the preferred way is to handle all material-polygons as one shape, and hole-polygons as other shape and use something like:
var shape = [lower_house_material, upper_house_material];
shape.holes = [lower_house_hole_1, lower_house_hole_2, upper_house_hole_1, upper_house_hole_2, upper_house_hole_3];
var 3d_geometry = THREE.ExtrudeGeometry( shape, extrusionSettings );
So the 3d_geometry should be at the end one mesh to which I can append a texture this way:
var textureFront = new THREE.ImageUtils.loadTexture( 'textureFront.png');
var textureSide = new THREE.ImageUtils.loadTexture( 'textureSide.png');
var materialFront = new THREE.MeshBasicMaterial( { map: textureFront } );
var materialSide = new THREE.MeshBasicMaterial( { map: textureSide } );
var materialArray = [ materialFront, materialSide ];
var faceMaterial = new THREE.MeshFaceMaterial(materialArray);
var final_mesh = new THREE.Mesh(3d_geometry, faceMaterial );
And one of the textures could be something like this (256x256px):
And texture applied:
And because the mesh is extruded, there is also 3D thickness on the above, but you got the idea of texturing.
I know that y-coordinates have to be flipped but it is a trivial task and not the point of my question, but if three.js has ready-made function for clipping y, it would be helpful.
I have spent hours to examine the three.js source code, examples and documentation, but because the most frequent word there is "todo", it cannot help much. And I'm very newbie to three.js, I would think that this may be trivial task for some experienced three.js user.
UPDATE: And just to make sure, the hole polygons are always well-behaved, which means that hole polygons are always fully inside material-polygons and there are no duplicate vertices or self-intersections either in material-polygons or hole-polygons and all material-polygons have CW winding order and holes CCW.
UPDATE: Merging geometries was not a solution for texturing the whole extruded polygon set by one texture: http://jsfiddle.net/C5dga. The texture is repeated on all individual shapes, so merging geometries in this case has no real meaning. The solution could be possibly found on merging shapes before they are extruded, but not found solution for this yet.
You can merge geometries as in the following snippet, resulting in just a single mesh. From your prior questions, you already know how to texture a single geometry.
var geometry1 = new THREE.ExtrudeGeometry( shape1, extrusionSettings );
var geometry2 = new THREE.ExtrudeGeometry( shape2, extrusionSettings );
geometry1.merge( geometry2 );
. . .
var mesh = new THREE.Mesh( geometry1, material );
scene.add( mesh );
Fiddle: http://jsfiddle.net/pHn2B/88/
Fiddle: http://jsfiddle.net/C5dga/13/ (with texture)
EDIT: As an alternative to creating separate geometries and using the merge utility, you can create a single geometry using the following pattern, instead:
var geometry1 = new THREE.ExtrudeGeometry( [ shape1, shape2 ], extrusionSettings );
EDIT: updated to three.js r.70

Integrating Poilu raphael boolean operations(union,substraction) with SVG-edit

I am doing a modification of svg-edit, more specifically Mark McKays Method draw: https://github.com/duopixel/Method-Draw.
I want to use this Raphael library i found: https://github.com/poilu/raphael-boolean that allows me to perform boolean(set) operations on paths within my canvas.
Now i have implemented a button within the editor that fires up a function:
var paper = Raphael("canvas", 250, 250);
var path = paper.path("M 43,53 183,85 C 194,113 179,136 167,161 122,159 98,195 70,188 z");
path.attr({fill: "#a00", stroke: "none"});
var ellipse = paper.ellipse(170, 160, 40, 35);
ellipse.attr({fill: "#0a0", stroke: "none"});
var newPathStr = paper.union(path, ellipse);
//draw a new path element using that string
var newPath = paper.path(newPathStr);
newPath.attr({fill: "#666"});
// as they aren't needed anymore remove the other elements
path.remove();
ellipse.remove();
Okay, upon clicking the button isnt the editor supposed to return a unioned(welded) path with an ellipse?
or am i getting this wrong?
i am figuring that something must change with the var paper = Raphael("canvas", 250, 250); line since svg-edit is using a different name for the canvas but i have no idea how to go about it.
Any help will be deeply appreciated as i have been struggling for sometime with this.
UPDATE: This library is unable to handle multi-object welding, self intersections and many other cases. It is only working if we want to perform operations on 2 simple objects. This might not be immediately relevant to the question at hand but i thought it is wise to mention it anyway.
Refer to this question if you are looking for Boolean Operations on SVG elements: Boolean Operations on SVG paths
The code you posted works in isolation, as shown here: http://jsfiddle.net/5SaR3/
You should be able to change the Raphael constructor line to something like:
var paper = Raphael(canvas);
where canvas is an object reference to the SVG element used by svg-edit.

How to create a `pixelized' SVG image from a bitmap?

I have a 16x16 bitmap and want to create an SVG that contains 16x16 squares with the colors of the pixels of the image. Is there an easy way to achieve this?
My current thoughts go into the direction of using Python and PIL to read the bitmap image and dynamically create an SVG image file with the corresponding objects. But this feels a little clumsy and like reinventing the wheel.
Is there a better way to do this?
If you don't need the output to be SVG, I would suggest using an HTML5 Canvas where you can sample the pixels of the image client-side (using getImageData() on the context) and then draw your own up-scaled image. Or, if you need SVG, you could still use Canvas for the image sampling and then use procedurally-created <rect/> elements in SVG for each pixel.
I've written an example using just HTML Canvas so you can see how to do this. In short:
function drawPixelated(img,context,zoom,x,y){
if (!zoom) zoom=4; if (!x) x=0; if (!y) y=0;
if (!img.id) img.id = "__img"+(drawPixelated.lastImageId++);
var idata = drawPixelated.idataById[img.id];
if (!idata){
var ctx = document.createElement('canvas').getContext('2d');
ctx.width = img.width;
ctx.height = img.height;
ctx.drawImage(img,0,0);
idata = drawPixelated.idataById[img.id] = ctx.getImageData(0,0,img.width,img.height).data;
}
for (var x2=0;x2<img.width;++x2){
for (var y2=0;y2<img.height;++y2){
var i=(y2*img.width+x2)*4;
var r=idata[i ];
var g=idata[i+1];
var b=idata[i+2];
var a=idata[i+3];
context.fillStyle = "rgba("+r+","+g+","+b+","+(a/255)+")";
context.fillRect(x+x2*zoom, y+y2*zoom, zoom, zoom);
}
}
};
drawPixelated.idataById={};
drawPixelated.lastImageId=0;
If you really need SVG involved, I'd be happy to write an example that dynamically generated that.
Edit: OK, I've created an SVG version just for fun and practice. :)
As an aside (from an initial misreading of your question) this demo file from ASVG3 their old SVG Examples Page shows how to use some complex compositing of many different effects to create pixelation on arbitrary vector data. Unfortunately the demo does not load in Chrome, having been hardwired to require the (now-discontinued) Adobe SVG Viewer.

Resources