Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement saveGif as a native p5 function #5709

Merged
merged 64 commits into from
Sep 5, 2022
Merged
Show file tree
Hide file tree
Changes from 49 commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
6f43aa8
initial commits!
jesi-rgb Jul 1, 2022
b19d572
proper behaviour for delay and seconds
jesi-rgb Jul 3, 2022
228ae55
change function from image.js to loading_displaying.js
jesi-rgb Jul 3, 2022
98be55f
change naming scheme
jesi-rgb Jul 3, 2022
f84cedc
we are now able to dowload gifs!
jesi-rgb Jul 3, 2022
99d1a0e
create documentation, example and fix bug
jesi-rgb Jul 4, 2022
4f0b3f6
latest changes
jesi-rgb Jul 8, 2022
9f066e0
solved problem with framerate
jesi-rgb Jul 14, 2022
fb8af2d
adding comments to saveGif function
jesi-rgb Jul 15, 2022
7f158a7
adding constrain to powof2 to match the requirements of omggif
jesi-rgb Jul 15, 2022
2674e40
some changes to sketch [not important]
jesi-rgb Jul 15, 2022
99def1e
fixed local palette being greater than 256 colors
jesi-rgb Jul 15, 2022
f7c3a87
palette is now properly sized
jesi-rgb Jul 15, 2022
0decfc0
minor changes to follow the project's structure
jesi-rgb Jul 15, 2022
8d1d934
mayor bugfix: we now save the whole canvas!
jesi-rgb Jul 16, 2022
6928988
sync commit
jesi-rgb Jul 19, 2022
739385c
using gifenc to successfully encode gifs!
jesi-rgb Jul 19, 2022
07ba8a4
remove async
jesi-rgb Jul 19, 2022
e137355
removing unnecesary code
jesi-rgb Jul 20, 2022
6632a24
undo weird change
jesi-rgb Jul 20, 2022
10dc926
removing and cleaning
jesi-rgb Jul 20, 2022
b495c67
using global palette
jesi-rgb Jul 20, 2022
f46d0eb
made a different global palette generator
jesi-rgb Jul 20, 2022
f775062
creating a global color palette
jesi-rgb Jul 20, 2022
f9e4735
fancier palette generation and manipulation
jesi-rgb Jul 21, 2022
01f3939
debuggnig and restructuring
jesi-rgb Jul 21, 2022
0326f85
minor changes
jesi-rgb Jul 22, 2022
7256b1c
solve bug with transparentIndex
jesi-rgb Jul 23, 2022
de52c27
push latest changes meeting
jesi-rgb Jul 23, 2022
647a7ca
add underscore to custom functions
jesi-rgb Jul 24, 2022
f222c9b
solve transparency bug
jesi-rgb Jul 24, 2022
592bb79
added babel plugin before uglify for ES6 support
jesi-rgb Jul 26, 2022
18aabac
improved user feedback through p html elements
jesi-rgb Jul 26, 2022
9e3cc61
Merge branch 'gif-encoding-mattdesl' into save-gif
jesi-rgb Aug 4, 2022
b0e07dc
add back old saveGif function as encodeAndDownloadGif
jesi-rgb Aug 4, 2022
540b2f2
uncomment omggif import
jesi-rgb Aug 4, 2022
8bd536e
Merge branch 'processing:main' into save-gif
jesi-rgb Aug 4, 2022
571f8a5
change ecmaVersion to accept async
jesi-rgb Aug 4, 2022
73acfb1
renamed functions across the repo
jesi-rgb Aug 6, 2022
ae03db0
better global palette and fixed index bug!
jesi-rgb Aug 8, 2022
629d744
adjust test from saveGif to encodeAndDownloadGif
jesi-rgb Aug 8, 2022
0ebf9b7
small styling change
jesi-rgb Aug 8, 2022
5664744
added support for WEBGL renderer!
jesi-rgb Aug 8, 2022
02c44be
created new globalPalette function
jesi-rgb Aug 9, 2022
7557cf8
refactoring and better palette generation
jesi-rgb Aug 10, 2022
bed8cfa
refactor arguments and support for all frameRates!
jesi-rgb Aug 11, 2022
79452ec
change typing in documentation example
jesi-rgb Aug 11, 2022
5b58200
add parameter validation through FES
jesi-rgb Aug 11, 2022
547b533
wrote initial tests
jesi-rgb Aug 11, 2022
57158e5
fix use of var in line 234
jesi-rgb Aug 17, 2022
5fd1766
remove initialization of arguments
jesi-rgb Aug 17, 2022
ebd2d61
parsing arguments and adding options object
jesi-rgb Aug 20, 2022
133eafa
remove unnecessary code and update example
jesi-rgb Aug 21, 2022
f42fef8
fix lint issue in example
jesi-rgb Aug 21, 2022
7daec8e
added more tests!
jesi-rgb Aug 21, 2022
c8baf6e
addressing mentor's feedback
jesi-rgb Aug 21, 2022
d9c5fe0
sanity and type checking for the arguments in options object
jesi-rgb Aug 22, 2022
7be6a62
better validation of arguments
jesi-rgb Aug 22, 2022
928ee37
improve example and add frameCount support
jesi-rgb Aug 22, 2022
64c4d7a
fix linter error in example
jesi-rgb Aug 22, 2022
95c4147
Merge branch 'processing:main' into save-gif
jesi-rgb Aug 22, 2022
8931fb1
put sketch back to normal
jesi-rgb Aug 22, 2022
79fd7c1
frameCount was missing this dot. also corrected alt text
jesi-rgb Aug 31, 2022
ac29642
update documentation for warning on function use
jesi-rgb Sep 2, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 18 additions & 4 deletions Gruntfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ module.exports = grunt => {
source: {
options: {
parserOptions: {
ecmaVersion: 5
ecmaVersion: 8
}
},
src: ['src/**/*.js']
Expand All @@ -163,7 +163,7 @@ module.exports = grunt => {
'eslint-samples': {
options: {
parserOptions: {
ecmaVersion: 6
ecmaVersion: 8
},
format: 'unix'
},
Expand Down Expand Up @@ -259,6 +259,16 @@ module.exports = grunt => {
}
}
},
babel: {
options: {
presets: ['@babel/preset-env']
},
dist: {
files: {
'lib/p5.pre-min.js': 'lib/p5.js'
}
}
},

// This minifies the javascript into a single file and adds a banner to the
// front of the file.
Expand All @@ -274,8 +284,8 @@ module.exports = grunt => {
},
dist: {
files: {
'lib/p5.min.js': 'lib/p5.pre-min.js',
'lib/modules/p5Custom.min.js': 'lib/modules/p5Custom.pre-min.js'
'lib/p5.min.js': ['lib/p5.pre-min.js'],
'lib/modules/p5Custom.min.js': ['lib/modules/p5Custom.pre-min.js']
}
}
},
Expand Down Expand Up @@ -523,10 +533,14 @@ module.exports = grunt => {
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-simple-nyc');

//this library converts the ES6 JS to ES5 so it can be properly minified
grunt.loadNpmTasks('grunt-babel');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i came across babelify, which seems to be compatible with existing bundler technology browserify.

We need a good walkthrough why we are not using babelify and instead simply using babel directly.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So sorry! Missed that one. I'll check if babelify works and remove babel!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After an extensive session of researching how to make babelify transpile the ES6 code to ES5, I could not get it to work. I don't know if I am doing something wrong.

With the grunt-babel package, the grunt command runs no problem. Without it, the uglify module spits out a problem saying "Unexpected token: '>'", which comes from the fact that there are some arrow functions in the code that should, by that point, been already transpiled to ES5 (no arrow functions). Either we can see how to make it work (I am probably missing something) or leave this as is, as it is a well-known, maintained package.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

taking jesus solution as of now and moving ahead...


// Create the multitasks.
grunt.registerTask('build', [
'browserify',
'browserify:min',
'babel',
'uglify',
'browserify:test'
]);
Expand Down
3 changes: 2 additions & 1 deletion lib/empty-example/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@
body {
padding: 0;
margin: 0;
background-color: #1b1b1b;
}
</style>
<script src="../p5.js"></script>
<script src="../p5.min.js"></script>
<!-- <script src="../addons/p5.sound.js"></script> -->
<script src="sketch.js"></script>
</head>
Expand Down
267 changes: 264 additions & 3 deletions lib/empty-example/sketch.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,268 @@
/* eslint-disable no-unused-vars */

// let saving = false;
// function setup() {
// // put setup code here
// createCanvas(500, 500);
// }

// function draw() {
// // put drawing code here
// let hue = map(sin(frameCount), -1, 1, 127, 255);
// let hue_2 = map(sin(frameCount / 100) + 0.791, -1, 1, 127, 255);

// strokeWeight(0);
// line(width / 2, 0, width / 2, height);
// line(0, height / 2, width, height / 2);

// fill(250, 250, 20);
// rect(0, 0, width / 2, height / 2);

// // fill(80, 80, hue);
// rect(width / 2, 0, width / 2, height / 2);

// fill(20, 250, 250);
// rect(0, height / 2, width / 2, height / 2);

// // fill(240, 240, 0);
// rect(width / 2, height / 2, width / 2, height / 2);

// fill(30);
// stroke(20, 250, 20);
// strokeWeight(4);
// circle(
// 100 * sin(frameCount / 20) + width / 2,
// // 100 * sin(frameCount / 20) + height / 2,
// // width / 2,
// height / 2,
// 100
// );

// if (saving) {
// save('frame' + frameCount.toString());
// }
// }

// function mousePressed() {
// if (mouseButton === RIGHT) {
// saveGif('mySketch', 1, 3);
// }
// }

// function keyPressed() {
// switch (key) {
// case 's':
// frameRate(3);
// frameCount = 0;
// saving = !saving;

// if (!saving) frameRate(60);
// break;
// }
// }

// / COMPLEX SKETCH
let offset;
let spacing;

function setup() {
// put setup code here
// randomSeed(1312);

w = min(windowHeight, windowWidth);
createCanvas(w, w);
print(w);
looping = false;
saving = false;
noLoop();

divisor = random(1.2, 3).toFixed(2);

frameWidth = w / divisor;
offset = (-frameWidth + w) / 2;

gen_num_total_squares = int(random(2, 20));
spacing = frameWidth / gen_num_total_squares;

initHue = random(0, 360);
compColor = (initHue + 360 / random(1, 4)) % 360;

gen_stroke_weight = random(-100, 100);
gen_stroke_fade_speed = random(30, 150);
gen_shift_small_squares = random(0, 10);

gen_offset_small_sq_i = random(3, 10);
gen_offset_small_sq_j = random(3, 10);

gen_rotation_speed = random(30, 250);

gen_depth = random(5, 20);
gen_offset_i = random(1, 10);
gen_offset_j = random(1, 10);

gen_transparency = random(20, 255);

background(24);
// saveGif('mySketch', 2);
}

function draw() {
// put drawing code here
}
colorMode(HSB);
background(initHue, 80, 20, gen_transparency);
makeSquares();
// addHandle();

if (saving) save('grid' + frameCount + '.png');
}

function makeSquares(depth = gen_depth) {
colorMode(HSB);
let count_i = 0;

for (let i = offset; i < w - offset; i += spacing) {
let count_j = 0;
count_i++;

if (count_i > gen_num_total_squares) break;

for (let j = offset; j < w - offset; j += spacing) {
count_j++;

if (count_j > gen_num_total_squares) break;

for (let n = 0; n < depth; n++) {
noFill();

if (n === 0) {
stroke(initHue, 100, 100);
fill(
initHue,
100,
100,
map(
sin(
gen_stroke_weight * (i + j) + frameCount / gen_stroke_fade_speed
),
-1,
1,
0,
0.3
)
);
} else {
stroke(compColor, map(n, 0, depth, 100, 0), 100);
fill(
compColor,
100,
100,
map(
cos(
gen_stroke_weight * (i + j) + frameCount / gen_stroke_fade_speed
),
-1,
1,
0,
0.3
)
);
}

strokeWeight(
map(
sin(
gen_stroke_weight * (i + j) + frameCount / gen_stroke_fade_speed
),
-1,
1,
0,
1.5
)
);

push();
translate(i + spacing / 2, j + spacing / 2);

rotate(
i * gen_offset_i +
j * gen_offset_j +
frameCount / (gen_rotation_speed / (n + 1))
);

if (n % 2 !== 0) {
translate(
sin(frameCount / 50) * gen_shift_small_squares,
cos(frameCount / 50) * gen_shift_small_squares
);
rotate(i * gen_offset_i + j * gen_offset_j + frameCount / 100);
}

if (n > 0)
rect(
-spacing / (gen_offset_small_sq_i + n),
-spacing / (gen_offset_small_sq_j + n),
spacing / (n + 1),
spacing / (n + 1)
);
else rect(-spacing / 2, -spacing / 2, spacing, spacing);

pop();
}
// strokeWeight(40);
// point(i, j);
}
}
}

function addHandle() {
fill(40);
noStroke();
textAlign(RIGHT, BOTTOM);
textFont(font);
textSize(20);
text('@jesi_rgb', w - 30, w - 30);
}

function mousePressed() {
if (mouseButton === LEFT) {
if (looping) {
noLoop();
looping = false;
} else {
loop();
looping = true;
}
}
}

function keyPressed() {
console.log(key);
switch (key) {
// pressing the 's' key
case 's':
saveGif('mySketch', 2);
break;

// pressing the '0' key
case '0':
frameCount = 0;
loop();
noLoop();
break;

// pressing the ← key
case 'ArrowLeft':
frameCount >= 0 ? (frameCount -= 1) : (frameCount = 0);
noLoop();
console.log(frameCount);
break;

// pressing the β†’ key
case 'ArrowRights':
frameCount += 1;
noLoop();
console.log(frameCount);
break;

default:
break;
}
}
11 changes: 11 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
"grunt-mocha-test": "^0.13.3",
"grunt-newer": "^1.1.0",
"grunt-simple-nyc": "^3.0.1",
"grunt-babel": "^8.0.0",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here we already use a library called babelify, which is i believe capable of transpiling the ecma versions.

why dont we take advantage of already setup tool like this instead adding grunt-bable as some new dependecy.

Copy link
Member Author

@jesi-rgb jesi-rgb Aug 18, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Main problem

To understand the main problem, let me explicitly state it here. The current build pipeline takes all the code and runs browserify on it, which makes a single file, and that is passed on to uglify, which makes it very small, browser ready.

Uglify does not understand ES6 code, so it needs to be transpiled before it gets here. This is browserify's (and more specifically,babelify's) job. Browserify can be coupled with babelify to transpile between javascript versions before compiling and bundling.

Babelify and options

Inside the tasks/build/browserify.js file we have, among other things, this important variable:

const babelifyOpts = {
  // add some options
  plugins: ['static-fs']
};

which lets us modify the behaviour of babelify. As of right now, it only has a plugin called static-fs, which converts all asynchronous file system calls to their "hardcoded" version, statically inlining these calls.

We can pass in more options to change the behaviour:

const babelifyOpts = {
  // add some options
  plugins: ['static-fs'],
  presets: ['@babel/preset-env'],
  global: true,
};

This uses the present-env preset, recommended for our use case and the global option tells babelify to also process and transpile code coming from node_modules. The default behavior does not do that.

No solution found

After playing with every combination, some problem arises.

  • If we don't include global, we cannot have node_modules transpiling so future dependencies from other contributors that also work similarly to gifenc won't work during build time.
  • If we don't include the static-fs, mochaChrome breaks:
Running "mochaChrome:yui" (mochaChrome) task
p5.js translator called before translations were loaded
p5.js translator called before translations were loaded
[stuck here forever]
  • Including static-fs AND global breaks the static-fs since it cannot compile other libraries (like opentype) with their file calls.
Fatal error: /Users/jesi/repositorios/p5.js/node_modules/opentype.js/dist/opentype.js: Not able to statically evaluate the expression(s) for babel-plugin-static-fs.
Try changing your source code to something that can be evaluated at build-time, e.g.
    const src = fs.readFileSync(__dirname + '/foo.txt', 'utf8');
 while parsing file: /Users/jesi/repositorios/p5.js/node_modules/opentype.js/dist/opentype.js

My initial solution

Initially, I simply included the grunt-babel option since the main problem was I could not get Uglify to work. This worked fine immediately, albeit not knowing why and not knowing that babelify was already present.

For what it's worth, the grunt-babel has not given absolutely any problem, since it is set after the browserify task which means it gets to transpile the already bundled app. I believe this can open a door for future contributors that won't have to deal with this topic but just develop a better experience for p5.js.

If this is not enough to justify the grunt-babel inclusion, my only guess is that I should not include gifenc at all and figure out another way of using that code. Totally open for discussion!

"html-entities": "^1.3.1",
"husky": "^4.2.3",
"i18next": "^19.0.2",
Expand All @@ -85,7 +86,8 @@
"regenerator-runtime": "^0.13.3",
"request": "^2.88.0",
"simple-git": "^3.3.0",
"whatwg-fetch": "^2.0.4"
"whatwg-fetch": "^2.0.4",
"gifenc": "^1.0.3"
},
"license": "LGPL-2.1",
"main": "./lib/p5.min.js",
Expand Down Expand Up @@ -154,7 +156,6 @@
"not dead"
],
"author": "",
"dependencies": {},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
Expand Down
Loading