Illustrator scripting JavaScript. have loop use number it is finding - javascript

I am trying to get coding to find a range of colors and select them.
Someone was able to help with this code.
with (app.activeDocument) {
if (pathItems.length > 0) {
alert(pathItems.length);
for (var g = 0 ; g < pathItems.length; g++) {
if (pathItems[g].filled == true) {
if (
pathItems[g].fillColor.red > 200 == true &&
pathItems[g].fillColor.red < 210 == true &&
pathItems[g].fillColor.green > 200 == true &&
pathItems[g].fillColor.green < 210 == true &&
pathItems[g].fillColor.blue > 200 == true &&
pathItems[g].fillColor.blue < 210 == true
)
{
alert('R' + pathItems[g].fillColor.red + ' G' + pathItems[g].fillColor.green + ' B' + pathItems[g].fillColor.blue);
}
}
}
}
}
I have been trying to get it to use the numbers it is finding for the alert and use those as the RGB colors to select
with (app.activeDocument) {
if (pathItems.length > 0) {
alert(pathItems.length);
for (var i = 0 ; i < pathItems.length; i++) {
if (pathItems[i].filled == true) {
if (
pathItems[i].fillColor.red > 200 == true &&
pathItems[i].fillColor.red < 210 == true &&
pathItems[i].fillColor.green > 200 == true &&
pathItems[i].fillColor.green < 210 == true &&
pathItems[i].fillColor.blue > 200 == true &&
pathItems[i].fillColor.blue < 210 == true
);
var newRGBColor = pathItems[i].fillColor.red &&
pathItems[i].fillColor.green &&
pathItems[i].fillColor.blue;
{
app.activeDocument.defaultFillColor = newRGBColor;
app.executeMenuCommand("Find Fill Color menu item");
}
}
}
}
}
But this is not working. I am very much of not a coder my brain just can understand it. I know the basics but, that is about all can my brain will retain. This is about my 100th version of this code. I am really trying, so please don't be mad at me if i am missing something.
I have taken a javascripting class, read the illustrator scripting guilds, and am all over W3schools. My brain just can not understand much for coding, so please please do not get mad at me. I really am just looking for help.
Updates
Thank you for letting me know with is out dated but, I got this coding from someone else as shown on top and i dont know enough to update things. What i am trying to get it to do is Find the RGB color breakdowns in Illustrator on objects. app.activeDocument.defaultFillColor will change my set my default color fill and app.executeMenuCommand("Find Fill Color menu item") will fine the color i have selected(default color if not one selected)
if (
pathItems[g].fillColor.red > 200 == true &&
pathItems[g].fillColor.red < 210 == true &&
pathItems[g].fillColor.green > 200 == true &&
pathItems[g].fillColor.green < 210 == true &&
pathItems[g].fillColor.blue > 200 == true &&
pathItems[g].fillColor.blue < 210 == true
)
This will find the colors i need to select but i can get my defaultFillColor to be the colors for the code before

I have the feeling we are having an XY problem here as it would be useful to know what your ultimate goal is.
Since you mention using the "Find Fill Color" menu item, I will assume you want the script to select all objects with colors in the given range (R, G and B between 200 and 210).
Modifying Mouser's script will give us:
var appActiveDocumentPathItems = app.activeDocument.pathItems; //create var for this object;
var selectionArray = []; //create array for objects to be selected
if (appActiveDocumentPathItems.length > 0) {
for (var i = 0; i < appActiveDocumentPathItems.length; i++) {
if (appActiveDocumentPathItems[i].filled == true) {
var fill = appActiveDocumentPathItems[i].fillColor;
if (
fill.red > 200 == true &&
fill.red < 210 == true &&
fill.green > 200 == true &&
fill.green < 210 == true &&
fill.blue > 200 == true &&
fill.blue < 210 == true
) {
selectionArray [selectionArray.length] = appActiveDocumentPathItems[i]; //if an object matches the requirement, add it to the array
}
}
}
}
activeDocument.selection = selectionArray; //select the objects in the array

This would very easily result in an unwanted value because of the operator &&. You want to combine the different values into one string that represents a RGB-colour:
take a look:
var pathItems = [{fillColor : {red : 200, green : 240, blue : 156}}]; //array - filled with colors
var newRGBColor = pathItems[0].fillColor.red && pathItems[0].fillColor.green && pathItems[0].fillColor.blue;
console.log(newRGBColor); //not the result expected : 156
To get it to work you must define the red, green and blue properties of the object RGBColor
var pathItems = [{
fillColor: {
red: 205,
green: 206,
blue: 209
}
}]; //array - filled with colors
var newRGBColor = new RGBColor() //initial default colour
newRGBColor.red = pathItems[0].fillColor.red;
newRGBColor.green = pathItems[0].fillColor.green;
newRGBColor.blue = pathItems[0].fillColor.blue;
console.log(newRGBColor);
//dummy function to make demo work, not part of the solution
function RGBColor()
{
this.red;
this.green;
this.blue;
}
Final solution (without with):
var appActiveDocumentPathItems = app.activeDocument.pathItems; //create var for this object;
if (appActiveDocumentPathItems.length > 0) {
for (var i = 0; i < appActiveDocumentPathItems.length; i++) {
if (appActiveDocumentPathItems[i].filled == true) {
var fill = appActiveDocumentPathItems[i].fillColor;
if (
fill.red > 200 == true &&
fill.red < 210 == true &&
fill.green > 200 == true &&
fill.green < 210 == true &&
fill.blue > 200 == true &&
fill.blue < 210 == true
) {
var newRGBColor = new RGBColor(); //this is how you define a new color in Illustrator's extendscript
newRGBColor.red = pathItems[i].fillColor.red;
newRGBColor.green = pathItems[i].fillColor.green;
newRGBColor.blue = pathItems[i].fillColor.blue;
app.activeDocument.defaultFillColor = newRGBColor;
app.executeMenuCommand("Find Fill Color menu item");
}
}
}
}
Remember that the this will loop all shapes that have a color filled and the last iteration of the loop (the last filled shape) will be the defaulFillColor. You need more logic to select a different color than the last one.

Related

Sudoku solution validator on Codewars

This code in JavaScript works, but can somebody explain to me what board[3][8] != board[8][3] does or to be more precise how does this piece of code checks if rows and columns numbers are not repeated. Thank you!
function validSolution(board) {
let count0 = 0;
let count1 = 0;
let count2 = 0;
let count3 = 0;
let count4 = 0;
let count5 = 0;
let count6 = 0;
let count7 = 0;
let count8 = 0;
let count9 = 0;
for (let i = 0; i < board.length; i++) {
count0 += board[i][0];
count1 += board[i][1];
count2 += board[i][2];
count3 += board[i][3];
count4 += board[i][4];
count5 += board[i][5];
count6 += board[i][6];
count7 += board[i][7];
count8 += board[i][8];
}
return (count0 === 45 && count1 === 45 && count2 === 45 && count3 === 45
&& count4 === 45 && count5 === 45 && count6 === 45 && count7 === 45 &&
count8 === 45 && board[3][8] != board[8][3] );
}
That additional check does make sure there's at least some variation in the numbers on the board, but it does not make sure the solution is actually valid.
Take this board for example:
let board = [
[5,5,5,5,5,5,5,5,5],
[5,5,5,5,5,5,5,5,5],
[5,5,5,5,5,5,5,5,5],
[4,5,5,5,5,5,5,5,6],
[5,5,5,5,5,5,5,5,5],
[6,5,5,5,5,5,5,5,4],
[5,5,5,5,5,5,5,5,5],
[5,5,5,5,5,5,5,5,5],
[5,5,5,5,5,5,5,5,5]
];
All of these rows have totals of 45, and grid positions [3][8] and [8][3] are not equal. validSolution incorrectly considers this board to be a valid solution...
I came up with another validation method:
function validSolution(board) {
var boardx = board.map(x => new Set(x).size == 9 ? x.reduce((a,b) => a+b, 0) : 0);
var boardy = board[0].map((x,col,b) => board.map(x => x[col])).map(x => new Set(x).size == 9 ? x.reduce((a,b) => a+b, 0) : 0);
return new Set(boardx).size == 1 && boardx[0] == 45 && new Set(boardy).size == 1 && boardy[0] == 45;
}
A little explanation:
boardx will calculate the sum of each row (x.reduce((a,b) => a+b, 0)) if it consists of 9 unique values (new Set(x).size == 9 1)
boardy will calculate the sum of each column, but first it has to 'rotate' the grid (board[0].map((x,col,b) => board.map(x => x[col])), followed by the same method we did earlier.
For a valid board, boardx and boardy should both consist of 9 elements of value 45. So, we check if both boardx and boardy only have 1 unique value, and that value is 45
1 The Set object can only store unique values, therefore it's a perfect tool to quickly check the number of unique elements in an array.
I don't think that [3][8] != [8][3] is necessary? In sudoku those two cells don't matter to each other.

how to combine elements of array into one alert box at the end

I have a program that calculates students percentages in a class and need the final output to all be in one box. however my program currently does on alert box for each student how do I fix this?
here is the code
<script>
function studentName(x)
{
while(x == '' || x >= 0 || x < 0)
{
if(x == '')
{
x = prompt('Cannot leave field blank. Enter again');
}
else if (x >= 0)
{
x = prompt('Cannot Enter a number. Enter again')
}
else
{
x = prompt('Cannot Enter a number. Enter again')
}
}
return(x)
}
function studentScore(y)
{
while(y == '' || y > 100 || y < 0 || isNaN(y))
{
if (y == '')
{
y = parseFloat(prompt("Cannot leave field, blank please enter students score"));
}
else if (y > 100 || y < 0)
{
y = parseFloat(prompt("Invalid score, please enter a score 0-100"));
}
else
{
y = parseFloat(prompt("Invalid score, please enter a score 0-100"));
}
}
return(y)
}
function another(z)
{
while(z == '' && z != 'n' && z != 'N' && z != 'y' && z != 'Y')
{
while (z == '' && z != 'n' && z != 'N' && z != 'y' && z != 'Y' )
{
z = prompt('Invalid response, would you like to enter another score Y/N ')
}
while(z == 'n' || z == 'N')
{
Z = prompt('Would you like to enter another student')
}
while (z == 'y' || z == 'Y')
{
z = prompt("Enter another score")
}
}
return(z)
}
var names = []
var scores = []
var redo = true
var anotherName
var redo2
var retry = true
var anotherScore
var retry2
var i = 0
var a = 1
var score = 0
while(redo == true)
{
var studentNames = prompt('Enter student name');
var name = studentName(studentNames);
names.push(name)
while(retry == true)
{
var studentScores = parseFloat(prompt('Enter student score'));
score = score + studentScore(studentScores);
retry = prompt('Enter another score? Y/N');
retry = another(retry); /**/
if(retry == 'y' || retry == 'Y')
{
retry = true
a++
}
else if(retry == 'n' || retry == 'N')
{
retry = false
}
}
score = score / a
scores[i] = score
redo = prompt('Enter another student? Y/N');
redo = another(redo); /**/
if(redo == 'y' || redo == 'Y')
{
redo = true
retry = true
i++;
a = 1
score = 0
}
else if(redo == 'n' || redo == 'N')
{
redo = false
}
}
var message = ""
for(y=0; y < names.length; y++)
{
alert(names[y] + " - " + scores[y]);
}
again I have a program that calculates students percentages in a class and need the final output to all be in one box. however my program currently does on alert box for each student how do i fix this and get all of the students names into one final alert box?
You are getting separate alerts because you're calling alert on every iteration with individual values. One solution could be to combine the names and the corresponding scores in a new array and call alert once on this array. Using join('\n') on the new array will convert the array elements to string and separate each array elements with new line, for the sake of formatting. Just change the last part with:
let roster = [];
for(let y=0; y < names.length; y++) {
roster.push(names[y] + " - " + scores[y]);
}
alert(roster.join('\n'))
Better yet if you save names and scores in one array from the beginning, like the roster. In this way you could avoid additional iteration at the end.

Is there a way I can make this into a function or anything more efficient

if (one >= 16 && one <= 20){
if (two >= 10){
Sum.innerHTML = "$2750";
} else {
Sum.innerHTML = "$2500";
}
} else if (one >= 20 && one <= 25){
if (two >= 10){
Sum.innerHTML = "$2500";
} else {
Sum.innerHTML = "$2250";
}
}
This is just a little of my code, its just a simple money calculator for something I'm doing with a friend, which doesn't really matter. I can't seem to think if there is any way I can make this more efficient by using a function of such because it just seems so much of the same code and is "dry" this is not all the code from it, there is so much of this... Don't know if anyone can help FYI this is in js and Sum is a paragraph id so just think of it as console log.
Thanks, Ethan
EDIT
https://codepen.io/anon/pen/bxgYWL?editors=10100
If you go onto that you can see all the code with all the commenting I could think to add to try and help you understand. Don't worry if it doesn't help and if there is no other way to make it efficient. It doesn't REALLY matter because it's just a private bit of code and shouldn't cause many problems.
You can use a data structure to hold the range boundaries, then loop over it.
var params = [
{ onelow: 16, onehigh: 20,
two: [ { low: -inf, high: 9, value: 2500 },
{ low: 10, high: +inf, value: 2750 }],
},
{ onelow: 21, onehigh: 25,
two: [ { low: -inf, high: 9, value: 2250 },
{ low: 10, high: +inf, value: 2500 }],
},
...
];
let done = false;
for(let i = 0; !done && i < params.length; i++) {
let param = params[i];
if (one >= param.onelow && one <= param.onehigh) {
let done = false;
for (let j = 0; j < param.two.length; j++) {
let param2 = param[j];
if (two >= param2.low && two <= param2.high) {
Sum.innerHTML = "$" + param2.value;
done = true; // stop the outer loop, too
break;
}
}
}
}
At first your this part of code:
if(two >= 10)
{
Sum.innerHTML = "$2750";
}
else
{
Sum.innerHTML = "$2500";
}
we could rewrite to more shorter code:
Sum.innerHTML = two >= 10 ? "$2750" : "$2500";
And then because your this part of code repeats all the time we could write it in function like follows:
function setSum(compareValue, ifValue, elseValue)
{
Sum.innerHTML = two >= compareValue ? ifValue : elseValue
// or even: Sum.innerHTML = two >= 10 ? ifValue : elseValue
}
And after this we can short your code from your example on codepen like in following example:
var numOne = document.getElementById("one"),
numTwo = document.getElementById("two"),
Sum = document.getElementById("sum");
numOne.addEventListener("input", calc);
numTwo.addEventListener("input", calc);
function setSum(compareValue, ifValue, elseValue)
{
Sum.innerHTML = two >= compareValue ? ifValue : elseValue
// or even: Sum.innerHTML = two >= 10 ? ifValue : elseValue
}
function calc()
{
var one = parseFloat(numOne.value) || 0,
two = parseFloat(numTwo.value) || 0;
if(one >= 16 && one <= 20)
setSum(10, "$2750", "$2500");
else if(one >= 20 && one <= 25)
setSum(10, "$2500", "$2250");
else if(one >= 25 && one <= 35)
setSum(10, "$2000", "$1750");
else if(one >= 35 && one <= 45)
setSum(10, "$1500", "$1250");
else if(one >= 45 && one <= 50)
setSum(10, "$1250", "$1000");
}
<h4>Calculator</h4>
<p>
<input id="one"> + <input id="two">
</p>
<p id="sum"></p>
I do not really understand what your calculater does, but you wrote:
This is just a little of my code, its just a simple money calculator for something I'm doing with a friend, which doesn't really matter.
You can optimize your code as following (UPDATED):
var temp = "$2500"
if (one >= 16 && one <= 20){
if (two >= 10){
temp = "$2750";
}
} else if (one >= 20 && one <= 25){
if (!(two >= 10)){
temp = "$2250";
}
}
Sum.innerHTML = temp;
You don't always need to write an else, it can be avoided like in the above code. If you have to change the value, then set one value that is going to be same always, and change the value if the condition is satisfied.
I hope my answer was meaningful. Thanks.

Google Custom Script to evaluate values and assign cell value

I am having trouble getting my script to function the way I want it to. What I want it to do is take 3 values, evaluate them against several arguments then pass a value into a specific cell based on the evaluation. So far I have this;
function myFunction() {
var f = SpreadsheetApp.getActiveSheet().getRange("B$8");
var l = SpreadsheetApp.getActiveSheet().getRange("B$14");
var m = SpreadsheetApp.getActiveSheet().getRange("B$20");
if(f >= 1 && l >= 1 && m >= 1) {
SpreadsheetApp.getActiveSheet().getRange('B$49').setValue('0');
} else if (f >= 2) {
SpreadsheetApp.getActiveSheet().getRange('B$49').setValue('0');
} else if (f == 1 && l == 0 && m == 0) {
SpreadsheetApp.getActiveSheet().getRange('B$49').setValue('1');
} else if (f == 0 && l >=1 && m >= 1) {
SpreadsheetApp.getActiveSheet().getRange('B$49').setValue('2');
} else if (f == 0 && l+m <= 4) {
SpreadsheetApp.getActiveSheet().getRange('B$49').setValue('3');
} else if (f == 0 && l == 0 && m <=4) {
SpreadsheetApp.getActiveSheet().getRange('B$49').setValue('4');
} else if (f == 0 && i == 0 && m == 0) {
SpreadsheetApp.getActiveSheet().getRange('B$49').setValue('5');
} else {
SpreadsheetApp.getUi().alert('There has been an error, please rate manually');
}
}
No matter what values I have in cells B8, B14 or B20 I get the error message.
I would also like it to be able to function in multiple columns, while rows stay the same (hense the '$' in ranges). Though I am not certain I am doing this correctly.
If anyone could take a look through my code and explain what I am doing wrong and help me understand how to fix it that would be greatly appreciated.
In the lines getting data, you need to add .getValue() like this:
SpreadsheetApp.getActiveSheet().getRange("B$8").getValue()
You have a typo in:
else if (f == 0 && i == 0 && m == 0)
I believe the i should be l.
Also, check your logic for the value '3'. Like it is you will never get to '4' or '5'.

How NOT to break a function/loop on returning a value (Js)

Hey so I am making a 2D tile game, or really I am just messing around. I have made the map from an array, where 0 represents nothing, and other characters represents a walkable tile.
var map=[["t","t","t","t","t","t","t","t","t","t","t","t","t","t","t","t","t","t","t","t"],
["l","1","b","b","b","b","b","b","b","b","b","b","b","b","b","b","b","b","b","r"],
["l","r","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","r"],
["l","1","t","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","l","r"],
["l","1","1","t","t","t","t","t","t","t","t","t","t","t","t","r","0","0","l","r"],
["l","b","b","b","b","b","b","b","b","1","1","b","b","b","b","b","t","t","b","r"],
["0","0","0","0","0","0","0","0","0","l","r","0","0","0","0","0","0","0","0","0"],
["0","0","0","0","0","0","0","0","0","l","r","0","0","0","0","0","0","0","0","0"],
["0","0","0","0","0","0","0","0","0","l","r","0","0","0","0","0","0","0","0","0"],
["0","0","0","0","0","0","0","0","l","1","1","r","0","0","0","0","0","0","0","0"],
["0","0","0","0","0","0","0","l","1","1","1","1","r","0","0","0","0","0","0","0"],
["t","t","t","t","t","t","t","1","1","1","1","1","1","t","t","t","t","t","t","t"]];
On screen it looks like this
You see my moveable character here as well.
Now I have come this far, and I'd like my character to collide with the empty tiles represented as the value of 0 in my map array.
This is my code for checking collision (brackets are correct in the script):
function collisioncheck(ind){
for(var i in map){
for(var j in map[i]){
if(yass==true){
if(map[i][j]==0){
if(ind==0 && playerPosX==j*32+32 && playerPosY>i*32-32 && playerPosY<i*32+32){
return false;
}else if(ind==1 && playerPosX==j*32-32 && playerPosY>i*32-32 && playerPosY<i*32+32){
return false;
}else if(ind==2 && playerPosY==i*32+32 && playerPosX>j*32-32 && playerPosX<j*32+32){
return false;
}else if(ind==3 && playerPosY==i*32-32 && playerPosX>j*32-32 && playerPosX<j*32+32){
return false;
}else{
return true;
}
}
}else{
return true;
}
}
}
var yass=false;
function exist(){
for(var i in map){
for( var j in map[i]){
if(map[i][j]==0){
yass=true;
break;
}
}
}
So, this works. But only for the first 0 in the map. My problem is that the return statements breaks the for-loop and function. So my character will not collide with any other blank tile but the first one.
I will have to rewrite this, but is there any smart solution to this?
Link to jsfiddle here (Character not visible)
You're on the right track, your loop only runs for one iteration because you always return something after an iteration. However, you should only call return when you know the final result, because - as you said - it will exit the function.
It is correct to call 'return false' right away after a collision is detected, because if the player collides with at least one block, then there is a collision. On the opposite, 'return true' should only be called when you are sure that there are no collisions at all on the entire board, and you need to test every block on the map before you can confirm this.
function collisioncheck(ind) {
for (var i in map) {
for (var j in map[i]) {
if (yass == true) {
if (map[i][j] == 0) {
if (ind == 0 && playerPosX == j * 32 + 32 && playerPosY > i * 32 - 32 && playerPosY < i * 32 + 32) {
return false;
} else if (ind == 1 && playerPosX == j * 32 - 32 && playerPosY > i * 32 - 32 && playerPosY < i * 32 + 32) {
return false;
} else if (ind == 2 && playerPosY == i * 32 + 32 && playerPosX > j * 32 - 32 && playerPosX < j * 32 + 32) {
return false;
} else if (ind == 3 && playerPosY == i * 32 - 32 && playerPosX > j * 32 - 32 && playerPosX < j * 32 + 32) {
return false;
}
// else: do nothing. (i.e. let the loop run for the next block)
}
} else {
return true;
}
}
}
return true;
}
What we do here is go through all the blocks, if we find a collision we return false and exit the function. We only reach the 'return true' statement if we went through all the blocks without finding any collision, which is exactly what you want.
You need to use continue instead of return in the last else of your main if/else block on line 15

Categories

Resources