I'm trying to generate the coordinates for a sphere with a given radius, but am only managing to produce a cylinder and I'm not really figuring out why. Here's my current code:
function makeSphere(radius){
var sphere3D = {};
var radiusX = radius + 0.5;
var radiusY = radius + 0.5;
var radiusZ = radius + 0.5;
var invRadiusX = 1 / radiusX;
var invRadiusY = 1 / radiusY;
var invRadiusZ = 1 / radiusZ;
var ceilRadiusX = Math.ceil(radiusX);
var ceilRadiusY = Math.ceil(radiusY);
var ceilRadiusZ = Math.ceil(radiusZ);
var nextXn = 0;
forX: for (var x = 0; x <= ceilRadiusX; ++x) {
var xn = nextXn;
nextXn = (x + 1) * invRadiusX;
var nextYn = 0;
forY: for (var y = 0; y <= ceilRadiusY; ++y) {
var yn = nextYn;
nextYn = (y + 1) * invRadiusY;
var nextZn = 0;
forZ: for (var z = 0; z <= ceilRadiusZ; ++z) {
var zn = nextZn;
nextZn = (z + 1) * invRadiusZ;
var distanceSq = lengthSq(xn, yn, zn);
if (distanceSq > 1) {
if (z == 0) {
if (y == 0) {
break forX;
}
break forY;
}
break forZ;
}
if (lengthSq(nextXn, yn, zn) <= 1 && lengthSq(xn, nextYn, zn) <= 1 && lengthSq(xn, yn, nextZn) <= 1) {
continue;
}
sphere3D[[x,y,z]] = true;
sphere3D[[-x,y,z]] = true;
sphere3D[[x,-y,z]] = true;
sphere3D[[x,y,-z]] = true;
sphere3D[[-x,-y,z]] = true;
sphere3D[[x,-y,-z]] = true;
sphere3D[[-x,y,-z]] = true;
sphere3D[[-x,-y,-z]] = true;
}
}
}
}
function lengthSq(x, y, z) {
return (x * x) + (y * y) + (z * z);
}
function lengthSq(x, z) {
return (x * x) + (z * z);
}
Which gives the following output.
Any ideas on where I'm messing up? Thanks in advance for your attention.
Here's an approach that might be easier to follow. You'll want to break your code up into four parts:
Generating a set of points p within a particular n-dimensional domain
Filtering the set of points to those q that are within 1 unit of a spherical surface defined by a radius and n-dimensional origin
Reflecting the set of points across each of the Cartesian axes intersecting at the origin to create the reflected set of points r
Adding the set of points r to an object nSphere
Below is a set of functions that address each of these concerns to create an n-sphere.
// 0-sphere of radius 5 centered at [6]
console.log(makeNSphere(5, 6)); // { r: [6 - 5], [6 + 5] }
// 2-sphere of radius 2 centered at [0, 0, 0]
console.log(makeNSphere(2, 0, 0, 0));
function makeNSphere (radius, ...origin) {
function onSurface (p) {
const d = distance(
p.map(
(x, i) => x - origin[i]
)
);
return Math.abs(d - radius) < 1;
}
const nSphere = {};
const ps = range(
...origin.map(
x => [x, x + radius + 1]
)
);
const reflection = reflect(...origin);
for (const q of where(ps, onSurface)) {
for (const r of reflection(...q)) {
nSphere[r] = true;
}
}
return nSphere;
}
function distance (p) {
let sum = 0;
for (const x of p) {
sum += x * x;
}
return Math.sqrt(sum);
}
function* range (constraints = [], ...rest) {
const base = rest.length === 0;
let begin = 0;
let end = Infinity;
let increment = 1;
switch (constraints.length) {
case 0: break;
case 1: [end] = constraints; break;
case 2: [begin, end] = constraints; break;
default: [begin, end, increment] = constraints; break;
}
for (let i = begin; i < end; i += increment) {
if (base) {
yield [i];
continue;
}
for (const a of range(...rest)) {
yield [i, ...a];
}
}
}
function* where (ps, predicateFn) {
for (const p of ps) {
if (predicateFn(p)) {
yield p;
}
}
}
function reflect (...axes) {
return function* recurse (x, ...rest) {
if (rest.length === 0) {
yield* base(x);
return;
}
for (const xs of recurse(...rest)) {
yield* base(x, ...xs);
}
}
function* base (x, ...rest) {
yield [x, ...rest];
const axis = axes[axes.length - rest.length - 1];
const y = axis - (x - axis);
if (x !== y) {
yield [y, ...rest];
}
}
}
Not sure if this solves you problem but you can't have 2 functions having the same name. In your case, the second lengthSq() will supersede the first one even if the parameters are different.
There is no native function overloading in Javascript. However you can try these suggestions if it important to stick with same function name that handle multiple parameters Function overloading in Javascript - Best practices
The alternative is to rename it as as lengthSqXZ(x, z) if you are using it elsewhere outside the code you have provided.
Related
Hello thanks to moluapple, I got this script from the adobe support community, I'm a beginner in javascript but I have a task to make, I have to paint numbers in each color of the image, actually, I see that in this solution brings me numbers in each layer, but doesn't work for the colors, can someone has a similar solution?
Let me add the expected result which is for each color on the picture the script adds a number to the color 1 for green 2 for yellow etc, let me show you.enter image description here
here is the original link
https://community.adobe.com/t5/illustrator-discussions/script-insert-text-number-in-the-middle-of-visible-bounds-of-the-each-object/td-p/8088914/page/3
(function() {
var doc = app.activeDocument,
lays = doc.layers,
WORK_LAY = lays.add(),
NUM_LAY = lays.add(),
i = lays.length - 1,
lay;
// main working loop
for (; i > 1; i--) {
//process each layer
lay = lays[i];
lay.name = lay.name + " Num:" + (i - 1); // i-1 as 2 layers beed added.
process(lay.pathItems, false);
process(lay.compoundPathItems, true); // if any
}
//clean up
NUM_LAY.name = "Numbers";
WORK_LAY.remove();
function process(items, isCompound) {
var j = 0,
b, xy, s, p, op;
for (; j < items.length; j++) {
// process each pathItem
op = items[j];
// add stroke
if (isCompound) {
strokeComPath(op);
} else {
!op.closed && op.closed = true;
op.filled = false;
op.stroked = true;
};
b = getCenterBounds(op);
xy = [b[0] + (b[2] - b[0]) / 2, b[1] + (b[3] - b[1]) / 2];
s = (
Math.min(op.height, op.width) < 20 ||
(op.area && Math.abs(op.area) < 150)
) ? 4 : 6; // adjust font size for small area paths.
add_nums(i - 1, xy, s);
}
}
function getMinVisibleSize(b) {
var s = Math.min(b[2] - b[0], b[1] - b[3]);
return Math.abs(s);
}
function getGeometricCenter(p) {
var b = p.geometricBounds;
return [(b[0] + b[2]) / 2, (b[1] + b[3]) / 2];
}
// returns square of distance between p1 and p2
function getDist2(p1, p2) {
return Math.pow(p1[0] + p2[0], 2) + Math.pow(p1[1] + p2[1], 2);
}
// returns visibleBounds of a path in a compoundPath p
// which is closest to center of the original path op
function findBestBounds(op, p) {
var opc = getGeometricCenter(op);
var idx = 0,
d;
var minD = getDist2(opc, getGeometricCenter(p.pathItems[0]));
for (var i = 0, iEnd = p.pathItems.length; i < iEnd; i++) {
d = getDist2(opc, getGeometricCenter(p.pathItems[i]));
if (d < minD) {
minD = d;
idx = i;
}
}
return p.pathItems[idx].visibleBounds;
}
function applyOffset(op, checkBounds) {
var p = op.duplicate(WORK_LAY, ElementPlacement.PLACEATBEGINNING),
// offset value the small the better, but meantime more slow.
offset = function() {
var minsize = Math.min(p.width, p.height);
if (minsize >= 50) {
return '-1'
} else if (20 < minsize && minsize < 50) {
return '-0.5'
} else {
return '-0.2' // 0.2 * 2 (both side ) * 50 (Times) = 20
}
},
xmlstring = '<LiveEffect name="Adobe Offset Path"><Dict data="I jntp 2 R mlim 4 R ofst #offset"/></LiveEffect>'
.replace('#offset', offset()),
TIMES = 100; // if shapes are too large, should increase the value.
if (checkBounds) {
// check its size only if it needs, because it's too slow
while (TIMES-- && getMinVisibleSize(p.visibleBounds) > 3) p.applyEffect(xmlstring);
} else {
while (TIMES--) p.applyEffect(xmlstring);
}
return p;
}
function getCenterBounds(op) {
var originalMinSize = getMinVisibleSize(op.visibleBounds);
var p = applyOffset(op, false);
if (getMinVisibleSize(p.visibleBounds) > originalMinSize) {
// in some cases, path p becomes larger for some unknown reason
p.remove();
p = applyOffset(op, true);
}
var b = p.visibleBounds;
if (getMinVisibleSize(b) > 10) {
activeDocument.selection = [p];
executeMenuCommand("expandStyle");
p = activeDocument.selection[0];
if (p.typename == "CompoundPathItem") {
b = findBestBounds(op, p);
}
}
p.remove();
return b;
}
function add_nums(n, xy, s) {
var txt = NUM_LAY.textFrames.add();
txt.contents = n;
txt.textRange.justification = Justification.CENTER;
txt.textRange.characterAttributes.size = s;
txt.position = [xy[0] - txt.width / 2, xy[1] + txt.height / 2];
}
function strokeComPath(compoundPath) {
var p = compoundPath.pathItems,
l = p.length,
i = 0;
for (; i < l; i++) {
!p[i].closed && p[i].closed = true;
p[i].stroked = true;
p[i].filled = false;
};
}
})();
I don't understand what exactly you're trying to gain. So here is a guess:
If you change the function process() this way:
function process(items, isCompound) {
var j = 0,
b, xy, s, p, op;
for (; j < items.length; j++) {
// process each pathItem
op = items[j];
c = op.fillColor; // <--- here
color = 'CMYK ' + // <--- here
[ Math.round(c.cyan), // <--- here
Math.round(c.magenta), // <--- here
Math.round(c.yellow), // <--- here
Math.round(c.black) // <--- here
].join(','); // <--- here
// add stroke
if (isCompound) {
strokeComPath(op);
} else {
!op.closed && op.closed = true;
// op.filled = false; // <--- here
// op.stroked = true; // <--- here
};
b = getCenterBounds(op);
xy = [b[0] + (b[2] - b[0]) / 2, b[1] + (b[3] - b[1]) / 2];
s = (
Math.min(op.height, op.width) < 20 ||
(op.area && Math.abs(op.area) < 150)
) ? 4 : 6; // adjust font size for small area paths.
// add_nums(i - 1 + color, xy, s);
add_nums(color, xy, s); // <--- here
}
}
It will put CMYK percents inside the shapes instead of numbers.
Here is the another modification of the main function process(). It's turned out that it can be done if you add & modify just two lines:
function process(items, isCompound) {
var j = 0,
b, xy, s, p, op;
for (; j < items.length; j++) {
// process each pathItem
op = items[j];
try { color = op.fillColor.spot.name } catch(e) { continue } // <-- HERE
// add stroke
if (isCompound) {
strokeComPath(op);
} else {
!op.closed && op.closed = true;
op.filled = false;
op.stroked = true;
};
b = getCenterBounds(op);
xy = [b[0] + (b[2] - b[0]) / 2, b[1] + (b[3] - b[1]) / 2];
s = (
Math.min(op.height, op.width) < 20 ||
(op.area && Math.abs(op.area) < 150)
) ? 4 : 6; // adjust font size for small area paths.
add_nums(color, xy, s); // <--- HERE
}
}
And the colors should be Spot Global colors. The script put in the middle of every shape a name of the swatch color.
Input:
Output:
I want to find the first same number in the same index on different for loops and print 'Yes' if the number match and 'No' if the number does not match.
const x1 = 2;
const v1 = 1;
// 2 + 1 = 3
const x2 = 1;
const v2 = 2;
// 1 + 2 = 3
// Complete the kangaroo function below.
function kangaroo(x1, v1, x2, v2) {
let jump1 = 0;
let jump2 = 0;
let jumps1 = 0;
let jumps2 = 0;
for(let i = x1; i <= 10; i += v1) {
jumps1 = jump1 + i;
console.log(jumps1)
}
console.log('---------------------> Hold on <--------------')
for(let i = x2; i <= 10; i += v2) {
jumps2 = jump2 + i;
console.log(jumps2)
}
if(jumps1 === jumps2) {
console.log('Yes');
} else {
console.log('No');
}
};
kangaroo(x1, v1, x2, v2);
Add hese variables before the loops:
const loop1Numbers = {};
const loop2Numbers = {};
Add this inside your first loop:
loop1Numbers[i] = jumps1;
Add this inside your second loop:
loop2Numbers[i] = jumps2;
And finally after the loops you can check if "YES" or "NO":
let isThereAnyNumberEqualInSameIndex = false;
Object.keys(loop1Numbers).forEach(key => {
if (loop1Numbers[key] === loop2Numbers[key]) isThereAnyNumberEqualInSameIndex = true
});
After that - isThereAnyNumberEqualInSameIndex contains true or false as you need
I found this better solution on the web I think is more simple. Thanks!
Here is the link if you want to check it out https://bmizepatterson.com/2018/10/01/the-kangaroo-problem/
function kangaroo(x1, v1, x2, v2) {
if (v1 < v2 || (x2 - x1) % (v1 - v2) !== 0) {
return 'NO';
} else {
return 'YES';
}
};
console.log(kangaroo(x1, v1, x2, v2));
I came across the Javascript YarnPattern program from Stanford's CS106AX. However, as I was going through the code, I have difficulty understanding some lines.
In the main function YarnPattern(), I don't quite understand the bold chunk written between the stars " ** ". I'm not quite sure what it means when the variables are initialised with thisPeg = 0 and nextPeg = -1.
Also, I'm not quite sure about the line "nextPeg = (thisPeg + DELTA) % pegs.length". I was wondering how this line of code can help reaching a suitable peg's index position.
It would be great if someone can provide a clear explanation with regards to these lines of code. Your help is much appreciated.
// Constants
const GWINDOW_WIDTH = 1000;
const GWINDOW_HEIGHT = 625;
const N_ACROSS = 80;
const N_DOWN = 50;
const DELTA = 113;
// main function
function YarnPattern() {
let gw = GWindow(GWINDOW_WIDTH, GWINDOW_HEIGHT);
let pegs = createPegArray(GWINDOW_WIDTH, GWINDOW_HEIGHT, N_ACROSS, N_DOWN);
**let thisPeg = 0;
let nextPeg = -1;
while (thisPeg !== 0 || nextPeg === -1) {
nextPeg = (thisPeg + DELTA) % pegs.length;
let p0 = pegs[thisPeg];
let p1 = pegs[nextPeg];
let line = GLine(p0.x, p0.y, p1.x, p1.y);**
line.setColor("Magenta");
gw.add(line);
thisPeg = nextPeg;
}
}
function createPegArray(width, height, nAcross, nDown) {
let dx = width / nAcross;
let dy = height / nDown;
let pegs = [ ];
for (let i = 0; i < nAcross; i++) {
pegs.push(Point(i * dx, 0));
}
for (let i = 0; i < nDown; i++) {
pegs.push(Point(nAcross * dx, i * dy));
}
for (let i = nAcross; i > 0; i--) {
pegs.push(Point(i * dx, nDown * dy));
}
for (let i = nDown; i > 0; i--) {
pegs.push(Point(0, i * dy));
}
return pegs;
}
function Point(x, y) {
if (x === undefined) {
x = 0;
y = 0;
}
return { x: x, y: y };
}
How can I create the starting positon on a coordinate plot and update (increment) the x value? Initially, position was given values of 2 and 5, but for testing purposes, I'd just like to update the x value by 1, but am getting the same values returned?
function boardCreate(rows, columns) {
for (let x = 0; x < rows; x++) {
for (let y = 0; y < columns; y++) {
boardCell(x, y);
}
}
}
function boardCell(x, y) {
board[x] = board[x] || [];
board[x][y] = x + " " + y;
}
var board = [];
boardCreate(10, 10);
let position = board[2][5];
function incrementX(position) {
position[1] = position[1] + 1;
return position;
}
incrementX(position);
console.log(position);
If I understand correctly, you need to increment the board coordinates based on the x and y values of the current position - this can be achieved by:
extracting the x and y values at the specified position
const [x, y] = position.split(' ');
incrementing x by one:
const nextX = Number.parseInt(x) + 1;
reading the new position value from new x value and exisiting y
return board[nextX][y]
Hope this helps!
function boardCreate(rows, columns) {
for (let x = 0; x < rows; x++) {
for (let y = 0; y < columns; y++) {
boardCell(x, y);
}
}
}
function boardCell(x, y) {
board[x] = board[x] || [];
board[x][y] = x + " " + y;
}
var board = [];
boardCreate(10, 10);
let position = board[2][5];
function incrementX(position) {
/* Ensure valid position specified before attempting increment */
if (!position) {
return;
}
/* Read x,y coordinates from array value */
const [x, y] = position.split(' ');
/* Parse x to number and increment it */
const nextX = Number.parseInt(x) + 1;
if (nextX >= board.length) {
console.error(`next x: ${ nextX } out of bounds`);
return;
}
/* Look up board value at next x, and same y coordinate */
return board[nextX][y]
}
console.log(position);
/* Testing */
let p = incrementX(position);
for (let i = 0; i < 10; i++) {
p = incrementX(p);
console.log(p);
}
I'm beginner in deep learning, and trying to understand how algorithms works, writing them using JavaScript. Now I'm working on JavaScript implementation of conv2d like Tensorflow does, and misunderstand how to handle different count of filters, I have succeeded for one output filter and multiple output, but I'm confused how to produce operations with multiple filters input e.g. 32 -> 64
Here is example of code using ndarray
:
const outCount = 32 // count of inputs filters
const inCount = 1 // count of output features
const filterSize = 3
const stride = 1
const inShape = [1, 10, 10, outCount]
const outShape = [
1,
Math.ceil((inShape[1] - filterSize + 1) / stride),
Math.ceil((inShape[2] - filterSize + 1) / stride),
outCount
];
const filters = ndarray([], [filterSize, filterSize, inCount, outCount])
const conv2d = (input) => {
const result = ndarray(outShape)
// for each output feature
for (let fo = 0; fo < outCount; fo += 1) {
for (let x = 0; x < outShape[1]; x += 1) {
for (let y = 0; y < outShape[2]; y += 1) {
const fragment = ndarray([], [filterSize, filterSize]);
const filter = ndarray([], [filterSize, filterSize]);
// agregate fragment of image and filter
for (let fx = 0; fx < filterSize; fx += 1) {
for (let fy = 0; fy < filterSize; fy += 1) {
const dx = (x * stride) + fx;
const dy = (y * stride) + fy;
fragment.data.push(input.get(0, dx, dy, 0));
filter.data.push(filters.get(fx, fy, 0, fo));
}
}
// calc dot product of filter and image fragment
result.set(0, x, y, fo, dot(filter, fragment));
}
}
}
return result
}
For test I'm using a Tenforflow as a source of true and it algorithm works correct but with 1 -> N. But my question how to add a support of multiple filters in input value like N -> M.
Could someone explain how to modify this algorithm to make it more compatible with Tensorflow tf.nn.conv2d
A lot of thanks.
You would need to add another for loop. You didn't specify all of your input shapes and dimensions so it's actually kind of hard to write it exactly but it would look like this.
// agregate fragment of image and filter
for (let fx = 0; fx < filterSize; fx += 1) {
for (let fy = 0; fy < filterSize; fy += 1) {
//addition
for (let ch = 0; ch < input.get_channels) {
const dx = (x * stride) + fx;
const dy = (y * stride) + fy;
fragment.data.push(input.get(0, dx, dy, ch));
filter.data.push(filters.get(fx, fy, ch, fo));
}
}
}