Change the color of objects with extendscript - javascript

I've had a look around and can't find anything to do this, so I think I'm probably asking the wrong question.
I have a series of objects on a page in illustrator with a specific colour in RGB, say its 255 red. I want to select items with this color or loop through to see if an item is this color and change it to a CMYK colour, say 75% grey
It done this but it always selects the item to change it
var currPageItem=app.activeDocument.activeLayer;
var myColour = new RGBColor("255,0,0")//initial default colour
var myGrey= new CMYKColor("0,0,0,75")//initial default grey
// Stepping through each item on the layer.
for (var i = 0; i < currPageItem.pageItems.length; i++) {
var currentItem = currPageItem.pageItems[i];
//$.writeln("Object name=", currentItem);
if (currentItem.RGBColor=myColour) {
$.writeln("Colour function",i);
};
}
I'd like to be able to change stroke colours also. Any help really appreciated, very stuck on this

Thanks for putting me in the correct direction Josh. I think I've now got it. Firstly the document color mode under the file menu has to be set to RGB. If this isn't done then as it script reads through the page items it checks them as CMYK and so doesn't recogniise any RGB values.
Also it check the values to a gazillion decimal places so these need rounding. Have made the following adjustments and this seems to work. Any other improvement most welcome
var layer = app.activeDocument.activeLayer;
var testColor = new RGBColor()//initial default colour
testColor.red = 180;
testColor.green = 93;
testColor.blue = 120;
var myGrey= new CMYKColor()//initial default grey
myGrey.black=75;
// Stepping through each item on the layer.
for (var i = 0; i < layer.pathItems.length; i++) {
var item = layer.pathItems[i];
$.writeln("Test colour ",Math.round( item.fillColor.red))
if (Math.round(item.fillColor.red) == testColor.red &&
Math.round(item.fillColor.green)== testColor.green &&
Math.round(item.fillColor.blue) == testColor.blue)
{
$.writeln("Color function",i );
item.fillColor = myGrey;
}
}

Using something similar for pre-press work.
I adapted the RGB version above convert all rich black to K only. Still experimenting with color values and will add a way to also check the stroke color.
Thank you! Was searching for a solution for a long time.
var layer = app.activeDocument.activeLayer;
var testColor = new CMYKColor(); //initial default colour
testColor.cyan = 62;
testColor.magenta = 62;
testColor.yellow = 62;
testColor.black = 62;
var myGrey = new CMYKColor(); //initial default grey
myGrey.black = 100;
// Stepping through each item on the layer.
for (var i = 0; i < layer.pathItems.length; i++) {
var item = layer.pathItems[i];
$.writeln("Test colour ", Math.round(item.fillColor.cyan));
if (Math.round(item.fillColor.cyan) > testColor.red &&
Math.round(item.fillColor.magenta) > testColor.magenta &&
Math.round(item.fillColor.yellow) > testColor.yellow &&
Math.round(item.fillColor.black) > testColor.black) {
$.writeln("Color function", i);
item.fillColor = myGrey;
item.selected = true;
}
}

This should give you a good start with what you're trying to accomplish. *There is probably a better way to code this, but the illustrator documentation doesn't seem to be too helpful in figuring out how to instantiate or compare colors.
var layer = app.activeDocument.activeLayer;
var testColor = new RGBColor(); // initial default colour
testColor.red = 255; // rest of the values default to 0
var greyColor = new CMYKColor(); // initial default grey
greyColor.black = 75; // rest of the values default to 0
for (var i=0; i<layer.pathItems.length; i++) {
var item = layer.pathItems[i];
if (item.fillColor.red === testColor.red &&
item.fillColor.blue === testColor.blue &&
item.fillColor.green === testColor.green)
{
item.fillColor = greyColor;
}
}

Related

Why is my maze generator not detecting if a cell has been visited in p5.js?

I am trying to make a maze generator, and almost everything is working so far. I have been able to set my position to a random pos, and then I repeat the standard() function. In the function, I add pos to posList, and then I choose a random direction. Next, I check if the cell has been visited by running through all of the posList vectors in reverse. I haven't executed the code that backtracks yet. If visited = false then I move to the square and execute the yet-to-be-made path() function. However, for some reason, the mover just doesn't detect if a cell has been visited or not. I am using p5.js. What am I doing wrong?
var posList = [];
var pos;
var tempo;
var boole = false;
var direc;
var mka = 0;
function setup() {
createCanvas(400, 400);
//Set up position
pos = createVector(floor(random(3)), floor(random(3)));
frameRate(1)
}
//Choose a direction
function direct(dire) {
if(dire === 0) {
return(createVector(0, -1));
} else if(dire === 1) {
return(createVector(1, 0));
} else if(dire === 2) {
return(createVector(0, 1));
} else {
return(createVector(-1, 0));
}
}
/foLo stands fo forLoop
function foLo() {
//If we have checked less than three directions and know there is a possibility for moving
if(mka < 4) {
//tempoRARY, this is what we use to see if the cell has been visited
tempo = createVector(pos.x + direct(direc).x, pos.y + direct(direc).y);
//Go through posList backwards
for(var i = posList.length - 1; i >= 0; i --) {
//If the cell has been visited or the cell is off of the screen
if(tempo === posList[i]) {
//Change the direction
direc ++;
//Roll over direction value
if(direc === 4) {
direc = 0;
}
//Re-execute on next frame
foLo();
//The cell has been visited
boole = false;
//Debugging
console.log(direc)
mka++;
} else if(tempo.x < 0 || tempo.x > 2 || tempo.y < 0 || tempo.y > 2) {
direc ++;
if(direc === 4) {
direc = 0;
}
foLo();
boole = false;
console.log(direc)
mka++;
}
}
//If it wasn't visited (Happens every time for some reason)
if(boole === true) {
//position is now the temporary value
pos = tempo;
console.log("works")
mka = 0;
}
}
}
function standard() {
//Add pos to posList
posList.push(pos);
//Random direction
direc = floor(random(4));
//Convert to vector
direct(direc);
foLo();
//Tracks pos
fill(255, 255, 0);
rect(pos.x*100+50, pos.y*100+50, 50, 50)
}
function draw() {
background(255);
fill(0);
noStroke();
//draw grid
for(var i = 0; i < 4; i ++) {
rect(i*100,0,50,350);
rect(0, i*100, 350, 50);
}
standard();
boole = true;
console.log(pos)
console.log(posList);
}
Your issue is on the line where you compare two vectors if(tempo === posList[i]) {: This will never be true.
You can verify that with the following code (in setup() for example):
const v1 = new p5.Vector(1, 0);
const v2 = new p5.Vector(1, 0);
const v3 = new p5.Vector(1, 1);
console.log(v1 === v2) // false
console.log(v1 === v3) // false
This is because despite having the same value v1 and v2 are referencing two different objects.
What you could do is using the p5.Vector.equals function. The doc has the following example:
let v1 = createVector(10.0, 20.0, 30.0);
let v2 = createVector(10.0, 20.0, 30.0);
let v3 = createVector(0.0, 0.0, 0.0);
print(v1.equals(v2)); // true
print(v1.equals(v3)); // false
This might not give you a working algorithm because I suspect you have other logical errors (but I could be wrong or you will debug them later on) but at least this part of the code will do what you expect.
Another solution is to use a Set instead of your list of positions. The cons of this solution is that you will have to adapt your code to handle the "out of grid" position situation. However when you want to keep track of visited items a Set is usually a great solution because the access time is constant. That this means is that to define is a position has already been visited it will always take the same time (you'll do something like visitedSet.has(positionToCheck), whereas with your solution where you iterate through a list the more cells you have visited to longer it will take to check if the cell is in the list.
The Set solution will require that you transform your vectors before adding them to the set though sine, has I explained before you cannot simply compare vectors. So you could check for their string representation with something like this:
const visitedCells = new Set();
const vectorToString = (v) => `${v.x},{$v.y}` // function to get the vector representation
// ...
visitedCells.add(vectorToString(oneCell)); // Mark the cell as visited
visited = visitedCells.has(vectorToString(anotherCell))
Also has a general advice you should pay attention to your variables and functions name. For example
// foLo stands fo forLoop
function foLo() {
is a big smell: Your function name should be descriptive, when you see your function call foLo(); having to find the comment next to the function declaration makes the code less readable. You could call it generateMaze() and this way you'll know what it's doing without having to look at the function code.
Same for
//tempoRARY, this is what we use to see if the cell has been visited
tempo = createVector(pos.x + direct(direc).x, pos.y + direct(direc).y);
You could simply rename tempo to cellToVisit for example.
Or boole: naming a boolean boole doesn't convey a lot of information.
That could look like some minor details when you just wrote the code but when your code will be several hundred lines or when you read it again after taking several days of break, you'll thank past you for taking care of that.

Programmatically creating labels for Chart.js in angular?

I am dynamically creating a line graph for a movie. The y axis is a number of values that is collected over the course of the movies length. The length of the movie is known before. My problem is that ChartJs needs meaningful labels. It also puts each label at the same point as a y axis coordinate so I have to have the same amount of labels as points on the y axis.
This is the code I tried to use to programmatically created an array of labels based on the length of the data that I am feeding ChartJS
var makeLabels = function(){
var numberOfSegments = $scope.data.length;
$scope.labelArray = [];
for (var i = 0; i = numberOfSegments; i++) {
var label = i * 10;
var labelString = label.toString();
$scope.labelArray.push(labelString);
};
}
Image
this is a graph without all of the y axis data.
I fixed it do display the way I want to. The function makeLabels() gets the length of the movie and adds a blank "" label for every label that is not a multiple of 10. I removed the grid lines with scaleShowGridLines: false in the ChartJsProvider.setOptions method.
var makeLabels = function(){
var segments = $scope.movieLength / 1;
s
for (var i = 0; i <= segments; i++) {
var modulus = i % 10;
if(modulus === 0){
$scope.labelsArray.push(i.toString());
} else {
$scope.labelsArray.push("");
};
};

Color Spectrum Optimization

I have the following implementation, it works and functional. I am checking if fname properties are same in the following javascript object, then I assign the same color for these paired objects.
Here is one javascript object sample:
{"value": 10,"series": 1,"category": "LG","fname": "","valueColor": ""},
However, I would like to use more distinguished colors, rather than very similar color, for example in the given fiddle, colors are almost all in green spectrum. Also I do not want to give any color value where value property equals to 0
Here is the core implementation
function colorSpectrum(N) {
var colorMap = [], inc = 50, start = 1000;
for (i = start; i < start+N*inc; i+=inc) {
var num = ((4095 * i) >>> 0).toString(16);
while (num.length < 3) {
num = "0" + num;
}
colorMap.push("#" + num);
}
return colorMap;
}
function process(data){
var map = {}, colorMap = colorSpectrum(data.length);
data.forEach(function(item, index){
if(!map.hasOwnProperty(item.fname)){
map[item.fname] = colorMap[index];
}
data[index].valueColor = map[item.fname];
});
return data;
}
FIDDLE
Try picking random colors
function colorSpectrum(N) {
var colorMap = [];
for (i = 0; i < N; i+=1) {
var color = getRndColor()
colorMap.push("#"+color);
}
return colorMap;
}
function getRndColor() {
var n = Math.floor(Math.random()*255*255*255);
var hex = Number(n).toString(16);
while(hex.length < 6) {
hex = "0"+hex;
}
return hex;
}
If you want a full range of colors from black to white, you need to change this part:
var colorMap = [], inc = 50, start = 1000;
for (i = start; i < start+N*inc; i+=inc) {
You see, the loop starts from 1000, which is the color #3e8, already green. The scale should go from 0 to 4095 (for 3-character values like #007, #abc, etc...), with having the increment based on the amount of data.
However, I'd suggest getting at least a little bit of the control by having all RGB components generated separately instead of the full HEX value right away.

All series data are equal highcharts

I have function for updating sensors. Idea is simple, i add new points from sensors (1 sensor = 1 line) and after appending I update charts. But something went wrong and all series equal to themself
seriesArr - just array of sensors like {"sensorId1", sensorId2 etc}
chartObj - object of charts like chart 1: { chart: highcharts,
seriesArr: seriesArr }
Simple explanations:
chartObject.chart.series[0].points = chartObject.chart.series[1].points = chartObject.chart.series[2].points = etc
My code:
updateAllCharts: function() {
var allCharts = this.charts;
console.log('charts');
for (var chart in allCharts) {
var chartObject = allCharts[chart];
for (var i = 0; i < chartObject.seriesArr.length; i++) {
var elemId = chartObject.seriesArr[i][0];
var val = this.sensors[elemId].get('value');
console.log(val);
if (val === undefined) { // if sensor cant get data
continue;
}
var now = new Date;
var x = Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(),
now.getUTCHours(), now.getUTCMinutes(), now.getUTCSeconds(), now.getUTCMilliseconds());
var y = parseFloat(val);
chartObject.chart.series[i].addPoint([x, y], false, false);
console.log(x, y);
console.log(chartObject.chart.series[i].data);
}
}
for (var chart in allCharts) {
var chartObject = allCharts[chart];
chartObject.chart.redraw();
}
}
Screen:
UPDATE:
ProcessedDataX and Y arrays are changing, but there problem with points array, I always get some weird (maybe cached) points. WTF.
Playground
If you wanna play, i've setted up here jsfiddle, but actually it probably doesn't work. Can't add points to highchart object with setInterval.
JSFIDDLE
UPDATE2:
Actually it works fine in jsfiddle but i don't know wtf is going on in my project.
UPDATE3:
Found, that the same last point adds to each series. For example, series[i].lastPoints = series[n] , where n = last iteration.
Works, only if i REDRAW EVERYTIME AFTER ADDING EACH POINT. so bad solution, so bad.
just put true in parameter after Point object
chartObject.chart.series[z].addPoint(Point, true, false);
To all chart.redraw() you need to set isDirty flag
so try to use this code:
for (var chart in allCharts) {
var chartObject = allCharts[chart];
chartObject.yAxis[0].isDirty = true;
chartObject.chart.redraw();
}

Assign random picked color with javascript

I have a grid in my page that I want to populate via javascript with 200 elements. The actual code that populate the .grid element is the following:
$(function () {
var $gd = $(".grid");
var blocks="";
for(i=0; i < 200; i++){
blocks += '<div class="block"></div>';
}
$gd.append(blocks);
});
What I'm trying to do now is assign to each element created a random picked color from a list. Lets say red, blue, yellow, green (unexpected eh?). I'd like the values to be the most random possible, and also to avoid the same color to be picked again twice consequentially (just to be clear, it's ok something like red-blue-red-green-blue and so on, NOT red-red-green-yellow).
Maybe this can help in the randomize process, Fisher–Yates Shuffle, but I don't how to implement the non-twice adjacent rule stated above (if it is possible to apply at all).
What would be the best way to achieve this result? I'm also wondering if I could apply a gradient to each .block instead of a flat hex color; I guess the better route for this would be to assign a random class to each element mapped in CSS for the gradient and so on..
If the above script can be optimized performance-wise, I apreciate any suggestions!
Additional info:
I'm using jQuery
The grid is composed with 20 elements per row, for 10 rows
The colors should be 4, but can be raised to 5-7 adding some neutral grey tones if it can help
Here is a pen to experiment with http://codepen.io/Gruber/pen/lDxBw/
Bonus feature request: as stated above I'd like to avoide duplicate adjacent colors. Is it possible to avoid this also "above and below"? I guess its very hard if not impossible to totally avoid this, but well if anyone can find a solution it would be awesome!
Something like this, where the "nope" marked element is prevented, while the "yep" diagonal marked are allowed:
$(function () {
var colors = ["red","blue","green","yellow"];
var $gd = $(".grid");
var previousColor;
var blocks="";
for(i=0; i < 200; i++){
var color = "";
while(color === previousColor) {
color= colors [Math.floor(Math.random()*colors .length)];
}
blocks += '<div class="block" style="color:' + color + '"></div>';
previousColor = color;
}
$gd.append(blocks);
});
First, I'd use classes for the colors:
CSS:
.red { background-color: red; }
.blue { background-color: blue; }
.green { background-color: green; }
.yellow { background-color: yellow; }
And then here's the javascript:
$(document).ready(function() {
var colors = ["red","blue","green","yellow"];
var $gd = $(".grid");
var previousColor;
var previousRow;
var rowSize = 10;
while(rowSize--) previousRow.push("none");
var blocks = "";
for(i=0; i < 200; i++){
var color = colors [Math.floor(Math.random()*colors .length)];
while((color == previousColor) || (color == previousRow[i%rowSize])) {
color = colors [Math.floor(Math.random()*colors .length)];
}
blocks += '<div class="block ' + color + '"></div>';
previousColor = color;
previousRow[i%rowSize] = color;
}
$gd.append(blocks);
});
I started off with something similar to MikeB's code but added a row element so we know what is above your current block.
The first thing I'd like to introduce is a filtered indexing function.
Given an array:
var options = ['red', 'green', 'blue', 'purple', 'yellow']; // never less than 3!
And a filter:
function filterFunc(val) {
var taken = { 'red': 1, 'blue': 1 };
return taken[val] ? 0 : 1;
}
We can take the nth item from the values permitted (==1) by the filter (not a quick way to do it, but until there is a performance constraint...):
// filteredIndex returns nth (0-index) element satisfying filterFunc
// returns undefined if insufficient options
function filteredIndex(options, filterFunc, n) {
var i=-1, j=0;
for(;j<options.length && i<n; ++j) {
i += filterFunc(options[j]);
if(i==n)
break;
}
return options[j];
}
So now we can pick up a value with index 2 in the filtered list. If we don't have enough options to do so, we should get undefined.
If you are populating the colours from, say, the top left corner, you can use as few as 3 colours, as you are only constrained by the cells above and to the left.
To pick randomly, we need to set up the filter. We can do that from a list of known values thus:
function genFilterFunc(takenValues) {
var takenLookup = {};
for(var i=0; i < takenValues.length; ++i) {
takenLookup[takenValues[i]] = 1;
}
var filterFunc = function(val) {
return takenLookup[val] ? 0 : 1;
};
return filterFunc;
}
We can choose a random colour, then, for a cell in a grid[rows][cols]:
function randomColourNotUpOrLeft(grid, row, col, options, ignoreColour) {
var takenlist = [];
if(row > 0 && grid[row-1][col] != ignoreColour) {
takenlist.push(grid[row-1][col]);
}
if(col > 0 && grid[row][col-1] != ignoreColour) {
takenlist.push(grid[row][col-1]);
}
var filt = genFilterFunc(takenlist);
var randomIndex = Math.floor(Math.random()*(options.length-takenlist.length));
var randomColour = filteredIndex(options, filt, randomIndex);
return randomColour;
}
Note here that the random index used depends on how many colours have been filtered out; if there are 4 left we can have 0-3, but if only 2 are left it must be 0-1, etc. When the adjacent cells are the same colour and/or we are near the boundary, there is less constraint about which colour is chosen. Finally fill in a grid:
function fillGridSpeckled(grid, options, nullColour) {
for(var row=0; row<grid.length; ++row) {
for(var col=0; col<grid[row].length; ++col) {
grid[row][col] = randomColourNotUpOrLeft(grid,row,col,options,nullColour);
}
}
}
I've put it all in this jsbin, along with a few bits to demo the code working.

Categories

Resources