outlining text in processing - processing

My goal is to obtain an outline of text that is 1 pixels wide.
It could look something like this: https://jsfiddle.net/Lk1ju9yw/
I can't think of a good way to go about this so I did the following (in pseudocode):
PImage img;
void setup() {
size(400, 400);
// use text() to write on the canvas
// initialize PImage img
// load pixels for canvas and img
// loop thru canvas pixels and look for contrast
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
// compare canvas pixels at x-y with its neighbors
// change respective pixel on PImage img so as not to disturb canvas
}
}
// update pixels and draw img over the canvas
img.updatePixels();
img(img, 0, 0);
}
In a nutshell, I wrote white text on a black background on the canvas, did some edge detection and drew the results on a PImage, then used the PImage to store the results. I guess I could have skipped the PImage phase but I wanted to see what the edge detection algorithm produced.
So this does a decent job of getting the outline but there are some problems:
The outline is sometimes 1+ pixels wide. This is a problem. Suppose I want to store the outline (ie. all the positions of the white pixels) in an ArrayList.
For example, if using the ArrayList I draw an ellipse at EVERY point along the outline, the result is ok. But if I want the ellipses spaced apart, the ellipse-outline becomes kind of rough. In the fiddle I provided, the left edge of the letter 'h' is 2 pixels wide. Sometimes the ellipse will be drawn at the inner pixel, sometimes at the outer. That kind of thing makes it look ugly.
Elements of the ArrayList might be neighbors in the ArrayList, but not on the PImage. If I want to draw a circle for every 10th ArrayList location, the result won't necessarily be spaced apart on the PImage.
Here is an example of how ugly it can be: https://jsfiddle.net/Lk1ju9yw/1/
I am quite sure I understand why this is happening. I just don't know how to avoid it.
I also believe there is a solution (a PFont method) in p5.js. I am comfortable using p5 but unless I have to (let's say, because of difficulty), I would rather use processing. I've also heard of some libraries in processing that can help with this. Partly, I am interested in the result, but I am also interested in learning if I can program a solution myself (with some guidance, that is).

You can get an outline of text very easily in P5.js, because text honors the fill and stroke colors. So if you call noFill() the text will not be filled in, and if you call stroke(0) the text will have a black outline.
function setup() {
createCanvas(400, 200);
noSmooth();
}
function draw() {
background(220);
textSize(72);
textAlign(CENTER);
noFill();
stroke(0);
text("hey", width/2, height/2);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.16/p5.js"></script>
Unfortunately this approach won't work in regular Processing, because it just uses the stroke color for text. I'm not totally sure about Processing.js, but my guess is it's the same as Processing.
If you draw this to a buffer (using createGraphics()), then you can iterate over the buffer to get a list of points that make up your outline.
Now, as for putting the points in the correct order, you're going to have to do that yourself. The first approach that occurs to me is to sort them and group them by letter.
For example, your algorithm might be:
Find the upper-left-most point. Add it to your list.
Does that point you just added have any neighbors? If so, pick one and add it to your list. Repeat this step until the point has no neighbors.
Are there any points left? If so, find the point closest to the one you just added, and add it to your list. Go to step 2.
This might not be perfect, but if you want something more advanced you might have to start thinking about processing the list of points: maybe removing points that have a left neighbor, for example. You're going to have to play around to find the effect you're looking for.
This was an interesting question., thanks for that. Good luck, sounds like a fun project.

Related

My mesh flips for a rotation smaller than math.pi

I am coming back since I am having this geometric problem that I am not familiar with on Unity.
For a f-zero style game, I have a collider box (white on the screen captures) which is the origin of my raycast, and is bound to the movement of the vehicle.
In the shown code, this is this.collider. I control its rotation via a traditional applymatrix and there is no problem.
Then, on top of that, I have the rendered body of the vehicle in this.meshes. It inherits the rotation of the collider box, but gets some extra rotation on its vertical axis to give a visual sliding dynamic during the hard turns.
It is separate from the collider to keep the vector.forward of the movement (and the raycast) not affected by the extra-rotation. This is purely visual.
My question is: what is the best way to implement it?
I tried different things, but, basically, if I copy the position and rotation of the collider, no problem. As soon as I try to add some extra rotation = this.driftRotation, my body flips when rotation.y value is less than -math.pi. I can adjust the value of the rotation by incrementing Math.PI (like in Unity), but it doesn't work here.
No clean solution found with applyMatrix neither, and not a lot of google answers on "vertical rotation flip mesh"... though I'm pretty sure this pissue is common.
Some code:
this.meshes.position.set(
this.collider.position.x,
this.collider.position.y,
this.collider.position.z);
this.meshes.rotation.x = this.collider.rotation.x;
this.meshes.rotation.y = this.collider.rotation.y + this.driftRotation;
this.meshes.rotation.z = this.collider.rotation.z;
Enclosed more explicit pictures:
Thank you
Marquizzo, that's precisely the point: the 3rd px follows the 2nd one, so I'm still turning right but rotation suddenly flips (again, when rotation.y reaches -PI).
Anyway, I fixed it by not trying to directly change rotation.y value, but playing with matrix. Just takes time to understand what does what.
For those who may face a similar pb, here is my temp solution, until I find sthing more performant:
this.meshes.matrix.identity();
if (Math.abs(driftAmount) > 0)
{
this.driftAxis.copy(this.driftDirection);
this.driftValue = js.Utils.lerp(this.driftValue, Math.sign(driftAmount) * 0.4, 0.05);
this.meshes.matrix.makeRotationAxis(this.driftAxis, this.driftValue);
}
else if (Math.abs(this.driftValue) > 0)
{
this.driftAxis.copy(this.driftDirection);
this.driftValue = js.Utils.lerp(this.driftValue, 0, 0.1);
if (Math.abs(this.driftValue) < 0.001)
{
this.driftValue = 0;
}
this.meshes.matrix.makeRotationAxis(this.driftAxis, this.driftValue);
}
this.meshes.applyMatrix(this.collider.matrix);
I had to add a driftAxis along a driftDrection, which is my axis for my vertical rotation.
For ref. I think this subject is +/- bound to the issue I had:
https://github.com/mrdoob/three.js/issues/1460
Now I have another issue, how to add another rotation to this.meshes on another axis, the forward one, for a rolling effect, because if I just add another makeRotationAxis in this code it just skips the first one. But that sounds less difficult to figure out, there must exist the equivalent of combineMatrix something...

Invisible, interactable objects in AS3 -- how to code efficient invisibility?

Alpha invisibility.
I currently define circular regions on some images as "hot spots". For instance, I could have my photo on screen and overlay a circle on my head. To check for interaction with my head in realtime, I would returnOverlaps and do some manipulation on all objects overlapping the circle. For debugging, I make the circle yellow with alpha 0.5, and for release I decrease alpha to 0, making the circle invisible (as it should be).
Does this slow down the program? Is there another way to make the circle itself invisible while still remaining capable of interaction? Is there some way to color it "invisible" without using a (potentially) costly alpha of 0? Cache as bitmap matrix? Or some other efficient way to solve the "hot spot" detection without using masks?
Having just a few invisible display objects should not slow it down that much, but having many could. I think a more cleaner option may be to just handle it all in code, rather then have actual invisible display objects on the stage.
For a circle, you would define the center point and radius. Then to get if anyone clicked on it, you could go:
var xDist:Number = circle.x - mousePoint.x;
var yDist:Number = circle.y - mousePoint.y;
if((xDist * xDist) + (yDist * yDist) <= (circle.radius * circle.radius)){
// mousePoint is within circle
} else {
// mousePoint is outside of circle
}
If you insist on using display objects to set these circular hit areas (sometimes it can be easier visually, then by numbers), you could also write some code to read those display objects (and remove them from being rendered) in to get their positions and radius size.
added method:
// inputX and inputY are the hotspot's x and y positions, and inputRadius is the radius of the hotspot
function hitTestObj(inputA:DisplayObject, inputX:int, inputY:int, inputRadius:int):Boolean {
var xDist:Number = inputX - inputA.x;
var yDist:Number = inputY - inputA.y;
var minDist:Number = inputRadius + (inputA.width / 2);
return (((xDist * xDist) + (yDist * yDist)) =< (minDist * minDist))
}
An alpha=0 isn't all that costly in terms of rendering as Flash player will optimize for that (check here for actual figures). Bitmap caching wouldn't be of any help as the sprite is invisible. There's other ways to perform collision detection by doing the math yourself (more relevant in games with tens or even hundreds of sprites) but that would be an overkill in your case.

Correct calculations of floats in OpenGL ES

I'm making a game in 3D. Everything is correct in my code, although I'm confused about one thing.
When I setting up my perspective (gluPerspective) I set it to zNear = 0.1f and zFar = 100.0f. So far so good. Now, I also wanna move things just in the x or y direction via glTranslate.... But, the origo starts in the absolute centrum of my screen. Like I have zFar and zNear, why isn't that properly to the x and y coordinates? Now it is like if I move my sprite -2.0f to left on x-axis and make glTranslate... to handle that, it almost out of screen. And the z-axis is not behave like that. That's make it a lot more difficult to handle calculations in all directions. It's quite hard to add an unique float value to an object and for now I just add these randomly to just make them stay inside screen.
So, I have problem calculate corrects value to each object. Have I missed something? Should I change or thinkig of something? The reason that this is important is because I need to know the absolute left and right of my screen to make these calculations.
This is my onSurfaceChanged:
public void onSurfaceChanged(GL10 gl, int width, int height) {
gl.glViewport(0, 0, width, height);
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity();
GLU.gluPerspective(gl, 45.0f, (float)width / (float)height,
0.1f, 100.0f);
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
}
Thanks in advance!
When you use gluPerspective you are transforming your coordinates from 3D world space into 2D screen space, using a matrix which looks at (0,0,0) by default (i.e. x= 0, y = 0 is in the center of the screen). When you set your object coordinates you are doing it in world space, NOT screen space.
If you want to effectively do 2D graphics (where things are given coordinates respective to their position on the screen you want to use gluOrtho2D instead.

Rotating a single image (but not the others) on an html5 canvas?

I have a sprite I'm animating on an html canvas using normal sprite sheet blitting. On certain key events I'd like to change the direction of the sprite (ie, flip it, or rotate it 180 degrees) without changing anything (the other sprites) on the canvas.
Does anyone know how to do this?
So I was having this issue with my game; I had cells for up, down, and left animations, but no right cells. So I needed to flip the left cells to draw the right cells.
For each sprite I keep track of it's current top and left in the canvas, as well as each cell's top and left in the sprite sheet.
I've seen previous answers showing a simple horizontal flip as just translating the origin and flipping (inverse scale) of the axes, BUT this does not take into account that with sprites, flipping the origin will mess up the sprite's registration point (its top and left on the canvas).
This issue manifested in the sprite being mirrored correctly, but it's position being off by the width of the sprite. I solved it by taking into account the width of the sprite. Notice how I'm using CanvasRenderingContext2D.prototype.drawImage with 9 arguments since I'm slicing a sprite out of a sprite sheet:
// check if we need to flip image (special case for right movement)
if(sprite.translated){
context.save();
context.translate(context.canvas.width, 0);
context.scale(-1, 1);
context.drawImage(sprite.sheet,
cell.left,
cell.top,
sprite.width,
sprite.height,
// secret sauce: change the destination's X registration point
context.canvas.width - sprite.left - sprite.width,
sprite.top,
sprite.width, sprite.height);
context.restore();
} else {
// Assumes cells are all the same width and height, set in sprite
context.drawImage(sprite.sheet, cell.left, cell.top, sprite.width,
sprite.height, sprite.left, sprite.top, sprite.width, sprite.height);
}
Note: I also could have done the math in the translate, since it's meant to simplify calculations elsewhere.
Simply redraw the sprite, with a rotate transformation. Transformations in HTML Canvas 2D Context
The canvas is just an off-screen buffer. It won't be cleared unless you tell it to, and nothing else will be changed unless you tell it to.
There's a bunch of different situations in which you may have to redraw the area of or around the sprite. Otherwise, you'll get a ghosting effect where part of the old sprite is still visible below the new drawing, or other drawings become obscured. Some reasons are:
Your sprite is partially transparent,
Your sprite is partially translucent,
Other drawings are made on top of your sprite,
Your sprite is non-rectangular,
You're doing flips that are not multiples of 90 degrees.
So that might be a bit more work, and there are several different approaches to doing that. You could simply redraw the entire scene, or just the specific objects at the location, perhaps using the clip method.
A completely different direction might be to use other HTML elements, img or div, with absolute positioning and CSS3 transformations. That's basically a bit of trickery to delegate the rendering of your scene to the browser.
While I appreciate Shtééf's answer, after a bit of research, I have found that rotating the canvas you are actually using to display doesn't seem to be ideal. The saving, rotating and restoring while trying to create complex animations (aka think Street Fighter 2 not astroids) causes the canvas to flicker in even Chrome.
I have found however a usable strategy. The idea here is that you actually create two canvases, one will be for your game and the other will be a backbuffer of sorts and it will be used to rotate or scale your sprites. You essentially transform the backbuffer canvas, draw the image in question, then transfer it to your main canvas and restore (or not) the backbuffer. In this manner, you only rotate the hidden canvas and only effect the sprite in question not the entire game board.
The code looks something like this (work in progress):
mainContext.clearRect(lastXpos, lastYpos, lastWidth, lastHeight);
backContext.clearRect(0, 0, lastWidth, lastHeight);
lastXpos = xpos;
lastYpos = ypos;
lastWidth = width;
lastHeight = height;
backContext.save();
//check the direction of the sprite
//turn the backContext to this direction
//SPRITE_INVERTED==-1
if (spriteXDirection == SPRITE_INVERTED || spriteYDirection == SPRITE_INVERTED)
{
var horScale = 0;
var verScale = 0;
if (spriteXDirection == SPRITE_INVERTED)
{
horScale = width;
}
if (spriteYDirection == SPRITE_INVERTED)
{
verScale = height;
}
backContext.translate(horScale, verScale);
backContext.scale(spriteXDirection, spriteYDirection);
}
//draw the sprite not we always use 0,0 for top/left
backContext.drawImage(animations[currentPlay].sheet,
animationX,
animationY,
width,
height, 0, 0, width, height);
//Get the image data from the back context
var image = backContext.getImageData(0, 0, width, height);
//flip the back context back to center - or not, I haven't decided how to optimize this yet.
backContext.restore();
//transfer the image to your main context
mainContext.putImageData(image, xpos, ypos);
This has saved me a lot of headaches in understanding how to translate my sprites without having everything on my gameboard move all over the place. It also seems to perform better then modifying the main context.
Why don't you use save() and restore
ctx.save(); // save current state
ctx.rotate(Math.PI); // rotate
ctx.drawImage(link,x,y,20,20); // draws a chain link or dagger
ctx.restore(); // restore original states (no rotation etc)
How to rotate one image in a canvas?

How do I get a flood fill algorithm to cope with closed circles?

I have an application that accepts images as input and removes the background on which the image was taken. For example, if you pass in an image of a book on a blanket, the resulting image will be just the book with a transparent background.
The problem I have is when you input an image that has a large empty space in it, e.g. an elastic band. The floodfill algorithm starts at the corners of the image and removes the background of the picture, but of course it never makes it into the interior of the elastic band.
Is there a way of implementing this such that I can take an image of a closed circle against a background and get back just the loop itself, with no background inside or outside it?
You could always resample the image after every flood fill, and restart it whenever you find a color that matches the original background.
Flood fill algorithms are designed to start in one spot, and from there fill a constrained area, an area of similar colors. The circle does not match that background color, so the fill algorithm doesn't "jump" it to find others.
The solution is to flood different areas.
Here is a very crude, recursive, slow flood fill algorithm (from memory, untested):
public void floodfill(Image img, int x, int y, Color oldColor, Color newColor) {
// Check boundary
if (img.contains(x, y)) {
// Get current pixel color
Color currentColor = img.getColor(x, y);
// Check color match
if (currentColor.equals(oldColor)) {
// Set to new color
img.setColor(x, y, newColor);
// Start again on each of the neighbors
floodFill(img, x - 1, y, oldColor, newColor);
floodFill(img, x + 1, y, oldColor, newColor);
floodFill(img, x, y - 1, oldColor, newColor);
floodFill(img, x, y + 1, oldColor, newColor);
}
}
}
This question, and its answers address a very similar problem.
you can figure out what the predominant color of the background is (Which you should be able to do since you're able to remove the background starting at the corners) and look for that color everywhere else in the image.

Resources