Skip to content

Commit

Permalink
feat: legibility metric (OVERSET method) #25
Browse files Browse the repository at this point in the history
  • Loading branch information
sergiomrebelo committed Jul 13, 2023
1 parent 0ac28a3 commit 87a967b
Show file tree
Hide file tree
Showing 10 changed files with 220 additions and 34 deletions.
122 changes: 122 additions & 0 deletions src/@evoposter/evaluator/src/Legibility.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/**
* Measure the legibility of the text in the poster
* it is related to the legibility of the sentence
* and not the typeface shapes
*
* Sérgio M. Rebelo
* CDV lab. (CMS, CISUC, Portugal)
* srebelo[at]dei.uc.pt
*
* v1.0.0 August 2018 (as part of evoPoster)
* v2.0.0 November 2020 (as part of evoPoster)
* v2.5.0 November 2021 (as part of evoPoster)
* v3.0.0 November 2023
*/

import {arrMean, map} from "./utils.js";

const modes = [
[`OVERSET`, 0]
]

export const compute = (sentencesLength = [], minSize, mode= 'OVERSET', maxLimitScale= 2) => {
console.log ("minSize", minSize);
let results = [];
let max = minSize * maxLimitScale;
for (let sentence of sentencesLength) {
let dif = minSize-sentence;
// OVERSET MODE
dif = calculateOverset(dif, max);
results.push(dif);
}

// calculate mean
const value = arrMean([...results]);
console.log ("minSize", minSize, "value", value, sentencesLength);
return value;
}


const calculateOverset = (value, max, maxConstraint = 0.5) => {
// only prejudice when text overfits the poster
// if dif bigger than 0
value = value >= 0 ? 0 : value;
// if dif lower than limit
value = value <= -max ? -max : value;
// transform in scale of 1 (bad) to 0 (good)
value = map (value, -max, 0, maxConstraint, 0);
return value;
}


/**
* async _assessLegibility (scale=2, assesMethod='softComingOut&WS', visualLimit=200, max=10000, min=500, weighted=true) {
* let tws = this.state.phenotype.ref.current.tws;
* let resByLine = [];
*
* for (let tw of tws) {
* let value = tw[0]-tw[1];
* if (tw[1] === 0) {
* //blank elements are good to layout
* value = 1;
* } else {
* // compensate in the same way when the text is to big or to small.
* if (assesMethod === 'minWhiteSpace') {
* value = Math.abs(value) > visualLimit ? visualLimit : value;
* }
* // only compensate texts that overfits the textbox
* else if (assesMethod === 'minComingOut') {
* value = value > 0 ? 0 : value; //if text inside poster
* value = value < -(visualLimit) ? -(visualLimit) : value; //if not
* }
* //compensate more when the text overfits that when it
* else if (assesMethod === 'hardComingOut&WS') {
*
* if (value < 0) { //if content outfits the poster, i.e. the value is negative
* value = visualLimit;
* } else { //if inside of poster
* if (Math.abs(value) < visualLimit) {
* const dif = Math.abs(value - visualLimit);
* const d = map(dif, 0, visualLimit, 1, 2);
* value = Math.abs(value) / d;
* } else {
* value = visualLimit;
* }
* }
* } else if (assesMethod === 'softComingOut&WS') {
* if (value < 0) {
* value = visualLimit;
* } else {
* if (Math.abs(value) > visualLimit) {
* const dif = Math.abs(value - visualLimit * 2);
* const d = map(dif, 0, visualLimit * 2, 1, 3);
* value = Math.abs(value) / d;
* } else {
* value = 0;
* }
* }
* }
*
* value = map(Math.abs(value), 0, visualLimit, 1, 0);
* }
*
* resByLine.push(value);
* }
*
* let w = Array(resByLine.length).fill(1/resByLine.length);
*
* if (weighted) {
* w = this.state.genotype[1].map(x => x[2]);
* let sumw = arrSum(w);
*
* w = w.map(x => x / sumw);
* }
*
* for (let l=0; l<resByLine.length; l++) {
* resByLine[l] = resByLine[l]*w[l];
* }
*
*
* return Math.pow(arrSum(resByLine),1.5);
* }
*/
4 changes: 4 additions & 0 deletions src/@evoposter/evaluator/src/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,12 @@
* v0.0.1 July 2023
*/

import * as Legibility from "./Legibility.mjs";

export const info = () => {
console.log ("Evaluator working");
}

export const legibility = Legibility.compute;

export { info as default };
8 changes: 8 additions & 0 deletions src/@evoposter/evaluator/src/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export const map = (value, minA, maxA, minB, maxB) => {
return minB + (maxB - minB) * ((value - minA) / (maxA - minA));
}

export const arrMean = (arr) => {
const sum = arr.reduce((a, b) => a + b, 0);
return (sum / arr.length) || 0;
}
1 change: 0 additions & 1 deletion src/client/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ window.draw = () => {
if (window.app.screen < 3) return null;
if (window.app.population.updated) {
push();
translate(-width/2, -height/2);
background(window.app.backgroundColor);
window.app.population.draw();
pop();
Expand Down
2 changes: 0 additions & 2 deletions src/client/components/inputs/DropDownList.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ export class DropDownList extends LitElement {
this.init = init;
this.onChange = onChange;
this.classList.add(...classList);

console.log (`init= ${this.init} ${id}`)
}

render() {
Expand Down
2 changes: 0 additions & 2 deletions src/client/components/inputs/TextArea.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ export class TextArea extends LitElement {
this.label = label;
this.classList.add(...classList);
this.onChange = onChange;

console.log(`inside`, label, content, id, onChange, classList);
}

set = (content) => {
Expand Down
2 changes: 0 additions & 2 deletions src/client/components/panels/EvolutionPanel.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,9 @@ export class EvolutionPanel extends LitElement {

#validateValue = (value, current, min, max = null) => {
let v = validateNumberInput(value, current);
console.log("max inside", max);
if (current !== v) {
if (max !== null) {
value = Math.min(max, value);
console.log("max inside 2", value);
}

current = Math.max(min, value);
Expand Down
44 changes: 31 additions & 13 deletions src/client/controllers/Population.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import Poster from "./Poster.js";

export class Population {
#typefaces;

constructor(params) {
this.size = params["evo"]["popSize"];
this.params = params;
Expand All @@ -13,12 +12,10 @@ export class Population {
this.#typefaces = [];
this.updated = true;

console.log("inside pop", params);

// this._data = data; // private variable new version
}

initialisation = () => {
initialisation = async () => {
this.updated = true;
for (let i=0; i<this.size; i++) {
const poster = new Poster(i, this.generation, this.params);
Expand All @@ -32,16 +29,23 @@ export class Population {
}
}
}
await this.draw();
}

/* evaluate = async () => {
for (let ind of this.population) {
ind.evaluate();
}
} */

toggleGrid = (show) => {
for (let poster of this.population) {
poster.toggleGrid(show);
}
this.updated = true;
}

draw = () => {
draw = async () => {
this.updated = false;

// verify if the necessary fonts are loaded
Expand All @@ -54,22 +58,36 @@ export class Population {

const n = this.population.length < Params.visiblePosters ? this.population.length : Params.visiblePosters;
let posX = 0, posY = 0;
for (let i=0; i<n; i++) {
for (let i=0; i<this.population.length; i++) {
const ind = this.population[i];
if (!ind.ready) {
// check if individuals are loaded
this.updated = true;
}
ind.draw(posX, posY);
posX += 1;
if (posX % Math.floor(width/Params.visualisationGrid.width) === 0) { // (Params.visualisationGrid.cols-1)
posX = 0;
posY += 1;
const pg = await ind.draw();

if (i < n) {
const sideX = width / Math.floor(width / Params.visualisationGrid.width);
const sideY = ind.genotype.grid.size.height + Params.visualisationGrid.marginY;
const x = posX * sideX + sideX / 2;
const y = posY * sideY + sideY / 2;

// draw posters on canvas
push();
translate(-width / 2, -height / 2);
imageMode(CENTER);
image(pg, x, y);
pop();

posX += 1;
if (posX % Math.floor(width / Params.visualisationGrid.width) === 0) { // (Params.visualisationGrid.cols-1)
posX = 0;
posY += 1;
}
}
}


// document.fonts.check("12px molot")
// await this.evaluate();
}
}

Expand Down
59 changes: 50 additions & 9 deletions src/client/controllers/Poster.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import {Params} from "../Params.js";
import backgroundStyles from "./BackgroundStyles.js";

import * as evaluator from "../../@evoposter/evaluator/src/index.mjs";



class Poster {
#showGrid = false;
#debug = true;
Expand All @@ -9,7 +13,9 @@ class Poster {
this.n = n;
this.generation = generation;
this.ready = false;

this.fitness = 1;
this.sentencesLenght = [];

// define grid
const grid = new Grid(
Expand Down Expand Up @@ -117,9 +123,8 @@ class Poster {
this.#showGrid = params.display.grid;
}

draw = async (posX = 0, posY=0) => {
draw = async () => {
this.ready = true;
push();
const pg = createGraphics(this.genotype.size.width, this.genotype.size.height);

// background
Expand All @@ -133,6 +138,9 @@ class Poster {
// typesetting typography on poster
await this.typeset(pg);

// evaluate the poster
await this.evaluate();

// debug
if (this.#debug) {
pg.textSize(10);
Expand All @@ -145,16 +153,31 @@ class Poster {
}

// place graphics
const sideX = width / Math.floor(width/Params.visualisationGrid.width);
const sideY = this.genotype.grid.size.height + Params.visualisationGrid.marginY;
const x = posX * sideX + sideX/2;
const y = posY * sideY + sideY/2;
imageMode(CENTER);
image(pg, x, y);
pop();
// const sideX = width / Math.floor(width/Params.visualisationGrid.width);
// const sideY = this.genotype.grid.size.height + Params.visualisationGrid.marginY;
// const x = posX * sideX + sideX/2;
// const y = posY * sideY + sideY/2;
// imageMode(CENTER);
// image(pg, x, y);
// pop();

return pg;
}

evaluate = () => {
this.fitness = 1;

// constraint
const legibility = evaluator.legibility(this.sentencesLenght, this.genotype.grid.getAvailableWidth());
// returns a number between 0 and 0.5
// subtracted to fitness
this.fitness -= legibility;
// this.fitness = Math.round(this.fitness*100)/100;
}

typeset = async(pg) => {
this.sentencesLenght = [];

pg.push();
pg.translate(pg.width/2, pg.height/2);
const ctx = pg.drawingContext;
Expand Down Expand Up @@ -184,6 +207,15 @@ class Poster {
drawingContext.font = `${tb["weight"]} ${getFontStretchName(tb['font-stretch'])} ${tb["size"]}px ${tb["typeface"]}`;
let content = tb["uppercase"] === true ? tb["content"].toUpperCase() : tb["content"];
pg.text(content, xPos, yPos);

// const sentenceWidth = pg.textWidth (content);
const sentenceWidth = ctx.measureText(content).width;

// debug
// pg.textSize(10);
// pg.fill (0)
// pg.text(sentenceWidth, xPos, yPos+15);
this.sentencesLenght.push(sentenceWidth);
}
pg.pop();
}
Expand Down Expand Up @@ -457,6 +489,15 @@ class Grid {
return 0;
}

getAvailableWidth = (margins = true) => {
if (margins) {
let availableWidth = this.size.width - (this.size.width * this.size.margin[0]) - (this.size.width * this.size.margin[2]);
return availableWidth;
} else {
return this.size.width;
}
}

width = (n, center = false, inMargin = false) => {
if (n < (this.v + 1) && n > 0) {
if (center) {
Expand Down
Loading

0 comments on commit 87a967b

Please sign in to comment.