How to determine shape fill color in AS3? - actionscript-3

I have a circle inside a MovieClip on stage with an instance name holder. I want to get color of that circle.
I am successfully targeting that circle as shown in code. I can change its color using transform, but I cannot find a way to actually check its color.
holder.getChildAt(0);

OK, as I said in my reply, at the end, this is doing what I need. It could be a bit simplified and optimized, but, as far as my needs are going, it is fast enough! :)
function decimalToHex(decimal: int, padding: int = 2): String {
var hex: String = Number(decimal).toString(16);
while (hex.length < padding) {
hex = "0" + hex;
}
return hex.toUpperCase();
}
function getColor(what: Object):void {
var obj = what.getChildAt(0);
var bmd: BitmapData = new BitmapData(obj.width, obj.height, true, 0x00ffffff);
//In the 0x00ffffff color, leading two zeros are alpha channel
bmd.draw(obj);
var img = new Bitmap(bmd);
var stp = "";
for (var l: uint = 0; l < img.height && stp == ""; l++) {
for (var m: uint = 0; m < img.width && stp == ""; m++) {
var color = decimalToHex(bmd.getPixel32(m, l), 8);
if (int(parseInt(color.substr(0, 2), 16)) > 0) {
color = "#" + color.substr(2);
stp = "stop";
// Display color in a text box named as "what"'s name + letter "c"
param.getChildByName(what.name + "c").text = color;
}
}
}
}
getColor(holder); //As in question, my shape is inside it's holder

Related

How to place each shapes right next to each other? And glow the shapes with thick outline

This is my sketch.js
// Standard "run once" component of Processing/P5JS idiom
function setup() {
createCanvas(400, 400);
background("#CCFFFF");
// Not providing any interactivity right now
noLoop();
console.log("Starting...");
myQuilt = new Quilt(4, 4, 100, color(0, 204, 128));
let blocks = [];
let backColor = color(176,224,230,1);
let color1 = color("#A60D41");
let color2 = color("#937109");
let color3 = color("#0B8710");
let color4 = color("#2B1C07");
let color5 = color("#FD9708");
let color6 = color("#FFFFFF");
// Create the 2d array
for (let row = 0; row < 4; row++) {
blocks[row] = new Array(4);
}
// Instantiate and add blocks to the Quilt
for (let row = 0; row < 4; row++) {
for (let col = 0; col < 4; col++) {
// I am adding a single kind of block here
// to each row and column. Add blocks you
// make and blocks you download from others
// in your Quilt.
if (row == 0) {
blocks[row][col] = new circles(100, 100, backColor, color2, color3);
} else if(row == 1)
{
blocks[row][col] = new LogCabin(100, 100, backColor, color1, color2);
}
else if(row == 2)
{
blocks[row][col] = new FlowerGarden(100, 100, backColor, color3, color4);
}
else if(row == 3)
{
blocks[row][col] = new EightPointStar(100, 100, backColor, color4, color5);
}
// Since I built a simple rotation flag into
// my FourPatch block, here I am rotating
// every third row. You do what you like.
myQuilt.addBlock(blocks[row][col], row, col);
}
}
// Draw it now
myQuilt.render();
}
This is what i have so far. I need to put in one line 1circle, 1Logcabin, then 1flowergarden, 1EighthPoint star.
![Text](https://i.stack.imgur.com/uDP5y.png
This Picture above is an example how i want mine to look like also the glowing colors.
![Text](https://i.stack.imgur.com/tCZJL.png

How to create a matrix in a loop for hittesting with colors without duplicate colors

I am trying to create a matrix with color hexagons that will allow for object hit-testing. This color matrix will sit behind a regular hexagon matrix(one without the matrix cells colored). I will mouse over and check for the color in the hidden matrix to indicate which object my mouse is over so I can simulate object detection. This should be straight forward but for some reason I cannot get the expected results.
I have a parent loop which loops through creating rows of the matrix. Each iteration calls the following function to paint the hexagon(which is defined in a collection of points made available through a hexagon service).
constructor(
private hexagons: HexagonService,
private state: StateService,
private randomColors: RandomColorService,
private drawingContextService: DrawingContextService) {
}
async DrawHexagonAsync(hidecolors: boolean): Promise<any> {
const color = this.randomColors.next();
let strokestyle = this.state.strokeStyle;
const context = this.drawingContextService.context;
console.log(color); // Look for duplicates in console
if (hidecolors) {
strokestyle = `#fff`;
}
context.strokeStyle = strokestyle;
context.lineWidth = this.state.lineWidth;
context.fillStyle = color;
context.lineJoin = 'round';
context.lineCap = 'round';
const points = this.hexagons.getHexagonPoints();
context.beginPath();
let x = this.drawingContextService.location.x + points[0].x;
let y = this.drawingContextService.location.y + points[0].y
context.moveTo(x,y);
// move to the start point
for (let i = 1; i < points.length; i++) {
x = points[i].x + this.drawingContextService.location.x;
y = points[i].y + this.drawingContextService.location.y;
context.lineTo(x, y);
context.stroke();
}
context.fill();
context.closePath();
}
At first glance it looks like it is working and creates a lovely hex matrix like the following:
At closer inspection, I noticed that some of the contiguous items get rendered as duplicate colors. Since, I need unique colors for hit-testing this will not do. I use a color service to iterate through and create unique colors.
// color service.
#Injectable({provideIn: 'root'})
export class RandomColorService {
colors: string[] = [];
index = 0;
constructor() {
while (this.colors.length < 5000) {
let random = () => {
let n = Math.random() * 256;
return Math.floor(n).toString(16);
}
// make sure there are no duplicate colors.
const color = `#${random()}${random()}${random()}`;
if (!this.colors.includes(color)) {
this.colors.push(color);
}
}
}
public next() {
if (this.index >= 5000) {
this.index = 0;
}
return this.colors[this.index++];
}
}
Initially, I put the checking code to make sure that the colors were unique but the same duplicate color problem occurs with or without the code check. The next function just returns the unique colors as needed and moves to the next.
I even put a console.log in the code to check and I don't see any duplicate log messages, yet the colors are clearly duplicates.
Using the following function against the colors clearly indicates that the colors are not similar(but in fact duplicate)
// Gets a pixel from the canvas and extracts the color.
onclick(evt) {
var pixelData = this.drawing.context.getImageData(evt.x, evt.y, 1, 1).data;
var hex = "#" + ("000000" + this.rgbToHex(pixelData[0], pixelData[1], pixelData[2])).slice(-6);
console.log(hex);
}
rgbToHex(r, g, b) {
if (r > 255 || g > 255 || b > 255)
throw "Invalid color component";
return ((r << 16) | (g << 8) | b).toString(16);
}
So naturally the question is, what am I missing?
The bug is in the color service. The numbers need to be padded with '0'. If the color is not 6 digits then the color change fails and the previous color is retained and used.
return Math.floor(n).toString(16).padStart(2, '0');
// color service.
#Injectable({provideIn: 'root'})
export class RandomColorService {
colors: string[] = [];
index = 0;
constructor() {
while (this.colors.length < 5000) {
let random = () => {
let n = Math.random() * 256;
return Math.floor(n).toString(16);
}
// make sure there are no duplicate colors.
const color = `#${random()}${random()}${random()}`;
if (!this.colors.includes(color)) {
this.colors.push(color);
}
}
}
public next() {
if (this.index >= 5000) {
this.index = 0;
}
return this.colors[this.index++];
}
}

Pairing a draggable object to a target object in AS3

I'm currently stuck with my approach below. I'm not entirely sure if using "hitTestObject" method is appropriate in pairing the pieces to their respective place. I was able to at least match the chess piece to their respective location (that's the best I can do and I feel i'm doing it wrong) but I'm now stuck in counting how many pieces are actually in their correct places. e.g. when I move the pawn to a different tile, it will still count as one, I also want to avoid duplicate counting, example, If pawn is already in the correct location, it will just count as 1, and if it was moved, then that count will be removed. Only count the pieces that are in the correct tile.
My goal here is to be able to make all the chess pieces draggable and determine if they're in their respective location. If ALL the chess pieces are in their location, it will trace or call a function.
Thank you!
import flash.events.Event;
import flash.display.MovieClip;
import flash.events.MouseEvent;
/* Declaring an X and Y variable to be used as a reset container */
var xPos: int, yPos: int;
/* Attaching event listeners for each chess piece */
addListeners(
king, queen, bishop_1, bishop_2, knight_1, knight_2, rook_1, rook_2,
pawn_1, pawn_2, pawn_3, pawn_4, pawn_5, pawn_6, pawn_7, pawn_8);
/* Getting the original x and y postion to be used as a reset */
function getPosition(currentTarget: Object): void {
xPos = currentTarget.x;
yPos = currentTarget.y;
}
/* Function to get the suffix value of an object. example, I need to get the value 4 from "pawn_4" */
function getLastCharInString($s: String, $pos: Number): String {
return $s.substr($s.length - $pos, $s.length);
}
/* A simple function that rotates the chess piece */
function lift(object: Object, rot: Number) {
object.rotation = rot;
}
function dragObject(e: MouseEvent): void {
getPosition(e.currentTarget);
lift(e.currentTarget, -10);
getChildByName(e.currentTarget.name + "_hs").alpha = 1;
e.currentTarget.startDrag();
}
/* This variable is supposed to hold the value of each piece that is correctly placed in each tile.
The total score should be 16 as there are 16 pieces. Only correcly placed piece should be added in the total score. */
var counter:int;
function stopDragObject(e: MouseEvent): void {
var curretTarget = e.currentTarget.name;
lift(e.currentTarget, 0);
/* Hide active hotspots */
getChildByName(e.currentTarget.name + "_hs").alpha = 0;
var multiplePieceSufix = Number(getLastCharInString(curretTarget, 1));
if (multiplePieceSufix >= 1) {
/* Boolean variables that checks whether the current piece is active*/
var isPawn: Boolean = false,
isBishop: Boolean = false,
isKnight: Boolean = false,
isRook: Boolean = false,
currentTargeName;
var widthDiff = getChildByName(e.currentTarget.name + "_hs").width - getChildByName(e.currentTarget.name).width / 2;
var heightDiff = getChildByName(e.currentTarget.name + "_hs").height - getChildByName(e.currentTarget.name).height / 2;
if (curretTarget.substr(0, 4) == "pawn") {
isPawn = true;
} else if (curretTarget.substr(0, 6) == "bishop") {
isBishop = true;
} else if (curretTarget.substr(0, 6) == "knight") {
isKnight = true;
} else if (curretTarget.substr(0, 4) == "rook") {
isRook = true;
}
if (isPawn == true) {
/* there are total of 8 pieces of pawn */
for (var w = 1; w < 9; w++) {
currentTargeName = this["pawn_" + w + "_hs"];
if (e.target.hitTestObject(currentTargeName)) {
/* For some reason the chess pieces are not aligning with their "_hs" version, I already checked their registry point and it seem to be normal.
so to fix, I had to manually add some hard coded values to adjust their location. */
e.currentTarget.x = currentTargeName.x - 8;
e.currentTarget.y = currentTargeName.y + currentTargeName.height;
}
}
} else if (isBishop == true) {
for (var x = 1; x < 3; x++) {
currentTargeName = this["bishop_" + x + "_hs"];
if (e.target.hitTestObject(currentTargeName)) {
e.currentTarget.x = currentTargeName.x - 9;
e.currentTarget.y = currentTargeName.y + currentTargeName.height - 18;
}
}
} else if (isKnight == true) {
for (var y = 1; y < 3; y++) {
currentTargeName = this["knight_" + y + "_hs"];
if (e.target.hitTestObject(currentTargeName)) {
e.currentTarget.x = currentTargeName.x - 8;
e.currentTarget.y = currentTargeName.y + currentTargeName.height;
}
}
} else if (isRook == true) {
for (var z = 1; z < 3; z++) {
currentTargeName = this["rook_" + z + "_hs"];
if (e.target.hitTestObject(currentTargeName)) {
e.currentTarget.x = currentTargeName.x - 8;
e.currentTarget.y = currentTargeName.y + 62;
}
}
}
} else {
if (e.target.hitTestObject(getChildByName(e.currentTarget.name + "_hs"))) {
/* Again, I'm not sure why the pieces are not aligning as intended.
modX and modY is a holder for the adjustment value. I'm not comfortable
seeing this approach myself, but I also run out of ideas how to fix it. */
var modX: Number, modY: Number;
if (e.currentTarget.name == "king") {
modX = 11;
modY = 53;
} else {
modX = 11;
modY = 29;
}
e.currentTarget.x = getChildByName(e.currentTarget.name + "_hs").x - modX;
e.currentTarget.y = getChildByName(e.currentTarget.name + "_hs").y + getChildByName(e.currentTarget.name + "_hs").height - modY;
}
}
/* This is supposed to add to the total score or count of how many pieces are placed correctly.
Thie problem with thi scounter, as it also counts any piece that is places to any "_hs" */
counter++;
trace(counter);
e.currentTarget.stopDrag();
}
function addListeners(...objects): void {
for (var i: int = 0; i < objects.length; i++) {
objects[i].addEventListener(MouseEvent.MOUSE_DOWN, dragObject);
objects[i].addEventListener(MouseEvent.MOUSE_UP, stopDragObject);
// hide hotspots
getChildByName( objects[i].name + "_hs" ).alpha = 0;
}
}
Source: Download the FLA here
--
Updates:
I have added comments in my code to clarify what I'm trying to accomplish.
I'm planning to do board game in flash which has similar function and behaviour to this. User can drag the object to a specified tile and check wether that object belongs there or not.
After reviewing your code, your question is quite broad. I'm going pair it down to what seems to be your main concern - the score / counting correctly moved pieces.
Right now, you do the following every time an object is dragged:
counter++;
This means that the counter will increment no matter where you drag the object, and no matter how times you drag the object. (so even if the piece was already in the correct spot, if you dragged it a second time it will still increment your counter).
What you need to do, is associate a flag with each object to indicate whether it is in the correct location or not, and set that flag to the appropriate value every time that object is done dragging.
Something like this:
//don't use target, use currentTarget
if (e.currentTarget.hitTestObject(currentTargeName)) {
e.currentTarget.correct = true; //since MovieClips are dynamic, you can just make up a property on them and assign a value to it.
//to fix your alignment:
e.currentTarget.x = currentTargeName.x + ((currentTargetName.width - e.currentTarget.width) * 0.5);
e.currentTarget.y = currentTargeName.y + currentTargeName.height;
}else{
//if the hit test is false, mark it as NOT correct
e.currentTarget.correct = false;
}
Then, later to know the current count, iterate over all the pieces and check their correct value. This would be much easier if all your pieces were in an array.
var allPieces:Array = [king, queen, bishop_1, bishop_2, knight_1, knight_2, rook_1, rook_2,
pawn_1, pawn_2, pawn_3, pawn_4, pawn_5, pawn_6, pawn_7, pawn_8];
function countCorrect():Boolean {
var ctr:int = 0;
for(var i:int=0;i<allPieces.length;i++){
if(allPieces[i].correct) ctr++;
}
return ctr;
}
trace(countCorrect() + " of " allPieces.length " are correct");
As an aside, this best way to do this would be with some custom class files. That would however require a complete refactoring of your code.
Also, you probably don't want to use hitTestObject, as even if a piece is mostly over a neighbor, it will still be true as long as 1 pixel of it's bound touch 1 pixel of the tile. Better would be to do a hitTestPoint on the tile, and pass in the center point of the piece (the the middle of the piece has to be touching the tile for it to count).
//a point that is the center of the events current target (the piece)
var point:Point = new Point();
point.x = e.currentTarget.x + (e.currentTarget.width * 0.5);
point.y = e.currentTarget.y - (e.currentTarget.height * 0.5);
if (currentTargetName.hitTestPoint(point)) {

Overflow text when i'm Fill text in canvas [duplicate]

I am trying to add text on an image using the <canvas> element. First the image is drawn and on the image the text is drawn. So far so good.
But where I am facing a problem is that if the text is too long, it gets cut off in the start and end by the canvas. I don't plan to resize the canvas, but I was wondering how to wrap the long text into multiple lines so that all of it gets displayed. Can anyone point me at the right direction?
Updated version of #mizar's answer, with one severe and one minor bug fixed.
function getLines(ctx, text, maxWidth) {
var words = text.split(" ");
var lines = [];
var currentLine = words[0];
for (var i = 1; i < words.length; i++) {
var word = words[i];
var width = ctx.measureText(currentLine + " " + word).width;
if (width < maxWidth) {
currentLine += " " + word;
} else {
lines.push(currentLine);
currentLine = word;
}
}
lines.push(currentLine);
return lines;
}
We've been using this code for some time, but today we were trying to figure out why some text wasn't drawing, and we found a bug!
It turns out that if you give a single word (without any spaces) to the getLines() function, it will return an empty array, rather than an array with a single line.
While we were investigating that, we found another (much more subtle) bug, where lines can end up slightly longer than they should be, since the original code didn't account for spaces when measuring the length of a line.
Our updated version, which works for everything we've thrown at it, is above. Let me know if you find any bugs!
A possible method (not completely tested, but as for now it worked perfectly)
/**
* Divide an entire phrase in an array of phrases, all with the max pixel length given.
* The words are initially separated by the space char.
* #param phrase
* #param length
* #return
*/
function getLines(ctx,phrase,maxPxLength,textStyle) {
var wa=phrase.split(" "),
phraseArray=[],
lastPhrase=wa[0],
measure=0,
splitChar=" ";
if (wa.length <= 1) {
return wa
}
ctx.font = textStyle;
for (var i=1;i<wa.length;i++) {
var w=wa[i];
measure=ctx.measureText(lastPhrase+splitChar+w).width;
if (measure<maxPxLength) {
lastPhrase+=(splitChar+w);
} else {
phraseArray.push(lastPhrase);
lastPhrase=w;
}
if (i===wa.length-1) {
phraseArray.push(lastPhrase);
break;
}
}
return phraseArray;
}
Here was my spin on it... I read #mizar's answer and made some alterations to it... and with a little assistance I Was able to get this.
code removed, see fiddle.
Here is example usage. http://jsfiddle.net/9PvMU/1/ - this script can also be seen here and ended up being what I used in the end... this function assumes ctx is available in the parent scope... if not you can always pass it in.
edit
the post was old and had my version of the function that I was still tinkering with. This version seems to have met my needs thus far and I hope it can help anyone else.
edit
It was brought to my attention there was a small bug in this code. It took me some time to get around to fixing it but here it is updated. I have tested it myself and it seems to work as expected now.
function fragmentText(text, maxWidth) {
var words = text.split(' '),
lines = [],
line = "";
if (ctx.measureText(text).width < maxWidth) {
return [text];
}
while (words.length > 0) {
var split = false;
while (ctx.measureText(words[0]).width >= maxWidth) {
var tmp = words[0];
words[0] = tmp.slice(0, -1);
if (!split) {
split = true;
words.splice(1, 0, tmp.slice(-1));
} else {
words[1] = tmp.slice(-1) + words[1];
}
}
if (ctx.measureText(line + words[0]).width < maxWidth) {
line += words.shift() + " ";
} else {
lines.push(line);
line = "";
}
if (words.length === 0) {
lines.push(line);
}
}
return lines;
}
context.measureText(text).width is what you're looking for...
Try this script to wrap the text on a canvas.
<script>
function wrapText(ctx, text, x, y, maxWidth, lineHeight) {
var words = text.split(' ');
var line = '';
for(var n = 0; n < words.length; n++) {
var testLine = line + words[n] + ' ';
var metrics = ctx.measureText(testLine);
var testWidth = metrics.width;
if (testWidth > maxWidth && n > 0) {
ctx.fillText(line, x, y);
line = words[n] + ' ';
y += lineHeight;
}
else {
line = testLine;
}
}
ctx.fillText(line, x, y);
}
var canvas = document.getElementById('Canvas01');
var ctx = canvas.getContext('2d');
var maxWidth = 400;
var lineHeight = 24;
var x = (canvas.width - maxWidth) / 2;
var y = 70;
var text = 'HTML is the language for describing the structure of Web pages. HTML stands for HyperText Markup Language. Web pages consist of markup tags and plain text. HTML is written in the form of HTML elements consisting of tags enclosed in angle brackets (like <html>). HTML tags most commonly come in pairs like <h1> and </h1>, although some tags represent empty elements and so are unpaired, for example <img>..';
ctx.font = '15pt Calibri';
ctx.fillStyle = '#555555';
wrapText(ctx, text, x, y, maxWidth, lineHeight);
</script>
</body>
See demo here http://codetutorial.com/examples-canvas/canvas-examples-text-wrap.
From the script here: http://www.html5canvastutorials.com/tutorials/html5-canvas-wrap-text-tutorial/
I've extended to include paragraph support. Use \n for new line.
function wrapText(context, text, x, y, line_width, line_height)
{
var line = '';
var paragraphs = text.split('\n');
for (var i = 0; i < paragraphs.length; i++)
{
var words = paragraphs[i].split(' ');
for (var n = 0; n < words.length; n++)
{
var testLine = line + words[n] + ' ';
var metrics = context.measureText(testLine);
var testWidth = metrics.width;
if (testWidth > line_width && n > 0)
{
context.fillText(line, x, y);
line = words[n] + ' ';
y += line_height;
}
else
{
line = testLine;
}
}
context.fillText(line, x, y);
y += line_height;
line = '';
}
}
Text can be formatted like so:
var text =
[
"Paragraph 1.",
"\n\n",
"Paragraph 2."
].join("");
Use:
wrapText(context, text, x, y, line_width, line_height);
in place of
context.fillText(text, x, y);
I am posting my own version used here since answers here weren't sufficient for me. The first word needed to be measured in my case, to be able to deny too long words from small canvas areas. And I needed support for 'break+space, 'space+break' or double-break/paragraph-break combos.
wrapLines: function(ctx, text, maxWidth) {
var lines = [],
words = text.replace(/\n\n/g,' ` ').replace(/(\n\s|\s\n)/g,'\r')
.replace(/\s\s/g,' ').replace('`',' ').replace(/(\r|\n)/g,' '+' ').split(' '),
space = ctx.measureText(' ').width,
width = 0,
line = '',
word = '',
len = words.length,
w = 0,
i;
for (i = 0; i < len; i++) {
word = words[i];
w = word ? ctx.measureText(word).width : 0;
if (w) {
width = width + space + w;
}
if (w > maxWidth) {
return [];
} else if (w && width < maxWidth) {
line += (i ? ' ' : '') + word;
} else {
!i || lines.push(line !== '' ? line.trim() : '');
line = word;
width = w;
}
}
if (len !== i || line !== '') {
lines.push(line);
}
return lines;
}
It supports any variants of lines breaks, or paragraph breaks, removes double spaces, as well as leading or trailing paragraph breaks. It returns either an empty array if the text doesn't fit. Or an array of lines ready to draw.
look at https://developer.mozilla.org/en/Drawing_text_using_a_canvas#measureText%28%29
If you can see the selected text, and see its wider than your canvas, you can remove words, until the text is short enough. With the removed words, you can start at the second line and do the same.
Of course, this will not be very efficient, so you can improve it by not removing one word, but multiple words if you see the text is much wider than the canvas width.
I did not research, but maybe their are even javascript libraries that do this for you
I modified it using the code from here http://miteshmaheta.blogspot.sg/2012/07/html5-wrap-text-in-canvas.html
http://jsfiddle.net/wizztjh/kDy2U/41/
This should bring the lines correctly from the textbox:-
function fragmentText(text, maxWidth) {
var lines = text.split("\n");
var fittingLines = [];
for (var i = 0; i < lines.length; i++) {
if (canvasContext.measureText(lines[i]).width <= maxWidth) {
fittingLines.push(lines[i]);
}
else {
var tmp = lines[i];
while (canvasContext.measureText(tmp).width > maxWidth) {
tmp = tmp.slice(0, tmp.length - 1);
}
if (tmp.length >= 1) {
var regex = new RegExp(".{1," + tmp.length + "}", "g");
var thisLineSplitted = lines[i].match(regex);
for (var j = 0; j < thisLineSplitted.length; j++) {
fittingLines.push(thisLineSplitted[j]);
}
}
}
}
return fittingLines;
And then get draw the fetched lines on the canvas :-
var lines = fragmentText(textBoxText, (rect.w - 10)); //rect.w = canvas width, rect.h = canvas height
for (var showLines = 0; showLines < lines.length; showLines++) { // do not show lines that go beyond the height
if ((showLines * resultFont.height) >= (rect.h - 10)) { // of the canvas
break;
}
}
for (var i = 1; i <= showLines; i++) {
canvasContext.fillText(lines[i-1], rect.clientX +5 , rect.clientY + 10 + (i * (resultFont.height))); // resultfont = get the font height using some sort of calculation
}
This is a typescript version of #JBelfort's answer.
(By the way, thanks for this brilliant code)
As he mentioned in his answer this code can simulate html element such as textarea,and also the CSS property
word-break: break-all
I added canvas location parameters (x, y and lineHeight)
function wrapText(
ctx: CanvasRenderingContext2D,
text: string,
maxWidth: number,
x: number,
y: number,
lineHeight: number
) {
const xOffset = x;
let yOffset = y;
const lines = text.split('\n');
const fittingLines: [string, number, number][] = [];
for (let i = 0; i < lines.length; i++) {
if (ctx.measureText(lines[i]).width <= maxWidth) {
fittingLines.push([lines[i], xOffset, yOffset]);
yOffset += lineHeight;
} else {
let tmp = lines[i];
while (ctx.measureText(tmp).width > maxWidth) {
tmp = tmp.slice(0, tmp.length - 1);
}
if (tmp.length >= 1) {
const regex = new RegExp(`.{1,${tmp.length}}`, 'g');
const thisLineSplitted = lines[i].match(regex);
for (let j = 0; j < thisLineSplitted!.length; j++) {
fittingLines.push([thisLineSplitted![j], xOffset, yOffset]);
yOffset += lineHeight;
}
}
}
}
return fittingLines;
}
and you can just use this like
const wrappedText = wrapText(ctx, dialog, 200, 100, 200, 50);
wrappedText.forEach(function (text) {
ctx.fillText(...text);
});
}

pixel bender 4-color shader

I have no experience with Pixel Bender, and shading languages seem like gibberish to me, so I was wondering if anybody could help me rewriting the following as3 code to function as a Pixel Bender filter/shader. The way it works is that I want to convert 16777215 colors into 4 color tones that I have defined in my palette array (lightest color first, darkest color last). The result is satisfactory, but the performance is bad, which is why I want to make a filter. Here's the code: (sbitmap being an image in my library)
var display:Bitmap = new Bitmap(new sbitmap());
var palette:Vector.<uint> = new <uint>[0x485B61, 0x4B8E74, 0xA6E76D, 0xD1FE85];
var data:BitmapData = display.bitmapData;
addChild(display);
const inc:int = int(0xFFFFFF/4)+1;
for(var i:int = 0; i < data.height; i++)
{
for(var j:int = 0; j < data.width; j++)
{
var color:uint = data.getPixel(j, i);
var pIndex:int = 0;
for(var k:int = 0; k < 0xFFFFFF; k += inc)
{
if(color >= k && color < k + inc)
{
data.setPixel(j, i, palette[pIndex]);
break;
}
pIndex++;
}
}
}
Here's a result I got: http://fc05.deviantart.net/fs70/f/2012/243/c/6/screen_shot_2012_08_30_at_1_19_10_pm_by_johnjensen-d5d1ms3.png
<languageVersion : 1.0;>
kernel test
< namespace : "Your Namespace";
vendor : "Your Vendor";
version : 1;
>
{
input image4 src;
output pixel4 dst;
void
evaluatePixel()
{
pixel4 k1=pixel4(0.282,0.71,0.38,1);
pixel4 k2=pixel4(0.294,0.557,0.455,1);
pixel4 k3=pixel4(0.65,0.91,0.475,1);
pixel4 k4=pixel4(0.82,0.99,0.52,1);
pixel4 s;
s = sampleNearest(src,outCoord());
if (s.r<0.25) dst=k1; else
if (s.r<0.5) dst=k2; else
if (s.r<0.75) dst=k3; else dst=k4;
}
}
Catch. The algorithm you use is based on red component only, so the filter only checks ".r" for 1-based float value.
The Pixel Blender language is not that hard. The very simple code below accomplishes what you're trying to do:
<languageVersion : 1.0;>
kernel untitled
< namespace : "Your Namespace";
vendor : "Your Vendor";
version : 1;
>
{
input image4 src;
output pixel4 dst;
void
evaluatePixel()
{
float4 px = sampleNearest(src,outCoord());
dst = px;
if(px.r < 0.25) {
dst = float4(0.25, 0.25, 0.25, 1.0);
} else if(px.r < 0.5) {
dst = float4(0.5, 0.5, 0.5, 1.0);
} else if(px.r < 0.75) {
dst = float4(0.75, 0.75, 0.75, 1.0);
} else {
dst = float4(1.0, 1.0, 1.0, 1.0);
}
}
}
Note that the RGBA values are floating point number from (0.0 to 1.0). A pixel is an array of four floats. You can write px[0], px[1], px[2], px[3] as px.r, px.g, px.b, px.a.
See the Pixel Blender reference for more details:
http://www.adobe.com/content/dam/Adobe/en/devnet/pixelbender/pdfs/pixelbender_reference.pdf