Skip to content

Commit

Permalink
Text colors and scaling
Browse files Browse the repository at this point in the history
  • Loading branch information
Bryan Parker committed Jul 6, 2024
1 parent 27f0b6c commit aad88c4
Show file tree
Hide file tree
Showing 8 changed files with 126 additions and 122 deletions.
9 changes: 4 additions & 5 deletions src/canvas.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Colors, rgbaToString } from '@/colors';
import { Colors, rgbaToString, parseColor } from '@/colors';
import { config } from '@/config';
import { Shape } from '@/shapes/shape';
import { PointShape } from '@/shapes/primitives/point_shape';
Expand Down Expand Up @@ -165,7 +165,7 @@ class HtmlCanvas implements Canvas {
this._ctx.font = `${text.fontSize() * text.currentScale()}px ${text.fontFamily()}`;
this._ctx.textBaseline = 'top';
this._ctx.textAlign = 'left';
this._ctx.fillStyle = rgbaToString(styles.color ?? styles.lineColor ?? Colors.black() )
this._ctx.fillStyle = rgbaToString(parseColor(styles.color ?? styles.lineColor ?? Colors.black()));

this._ctx.fillText(text.text(), ...this.t.translateRelative([xDraw, yDraw]));
}
Expand Down Expand Up @@ -295,15 +295,14 @@ class HtmlCanvas implements Canvas {
}

private setContextStyles(shape: Shape): void {
// const styles = isShape(shape) ? shape.styles() : shape;
const styles = shape.styles();

if (styles.color) {
this._ctx.fillStyle = rgbaToString(styles.color);
this._ctx.fillStyle = rgbaToString(parseColor(styles.color));
}

if (styles.lineColor) {
this._ctx.strokeStyle = rgbaToString(styles.lineColor);
this._ctx.strokeStyle = rgbaToString(parseColor(styles.lineColor));
}

if (styles.lineWidth) {
Expand Down
2 changes: 1 addition & 1 deletion src/colors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ function parseColor(color: string | RGBA): Readonly<RGBA> {
* @param color The RGBA color
* @returns the rgba string
*/
function rgbaToString(color: RGBA): string {
function rgbaToString(color: RGBA | Readonly<RGBA>): string {
return `rgba(${color[0]}, ${color[1]}, ${color[2]}, ${color[3]})`;
}

Expand Down
2 changes: 1 addition & 1 deletion src/shapes/derived/triangle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ type TriangleArgs = { x?: number; y?: number; center?: Locatable; width?: number


class Triangle extends PointShape {
constructor({ x = 0, y = 0, center = undefined, width = 1, height = 1, ...styleArgs }: TriangleArgs = {}) {
constructor({ x = 0, y = 0, center = undefined, width = 2, height = 2, ...styleArgs }: TriangleArgs = {}) {
super({ points: Triangle.trianglePoints(x, y, width, height), ...styleArgs });
}

Expand Down
20 changes: 13 additions & 7 deletions src/shapes/primitives/text.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,7 @@ class Text implements Shape {
}

this._textMeasurement = new TextMeasurement(config.canvasInstance!, this._tex);
this._textWidth = this._textMeasurement.textWidth(this._text, this._size, this._font);
this._textHeight = this._textMeasurement.textHeight(this._text, this._size, this._font);
[this._textWidth, this._textHeight] = this.updateDimensions();
}

center(): Point {
Expand Down Expand Up @@ -121,6 +120,7 @@ class Text implements Shape {

scale(factor: number): this {
this._scale += factor;
this.updateDimensions();
return this;
}

Expand Down Expand Up @@ -270,8 +270,7 @@ class Text implements Shape {

changeFontSize(size: number): this {
this._size = size;
this._textWidth = this._textMeasurement.textWidth(this._text, this._size, this._font);
this._textHeight = this._textMeasurement.textHeight(this._text, this._size, this._font);
this.updateDimensions();
return this;
}

Expand Down Expand Up @@ -299,9 +298,7 @@ class Text implements Shape {
changeText(text: string): this {
this._text = text;

this._textMeasurement = new TextMeasurement(config.canvasInstance!, this._tex);
this._textWidth = this._textMeasurement.textWidth(this._text, this._size, this._font);
this._textHeight = this._textMeasurement.textHeight(this._text, this._size, this._font);
this.updateDimensions();

return this;
}
Expand All @@ -317,6 +314,15 @@ class Text implements Shape {
maxY: top + this._textHeight
};
}

private updateDimensions(): [number, number] {
const { width, height } = this._textMeasurement.textDimensions(this._text, this._size, this._font, this._scale);
this._textWidth = width;
this._textHeight = height;

return [this._textWidth, this._textHeight];
}
}


export { Text, type TextBaseline, type TextAlign, type TextArgs };
66 changes: 27 additions & 39 deletions src/shapes/primitives/text_measurement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,24 @@ class TextMeasurement {
this._tex = tex;
}

textWidth(text: string, size: number, font: string): number {
textWidth(text: string, size: number, font: string, scale: number = 1): number {
return this._tex
? this.texTextDimensions(text, size).width
: this.textDimensions(text, size, font).width;
? this.texTextDimensions(text, size, scale).width
: this.textDimensions(text, size, font, scale).width;
}

textHeight(text: string, size: number, font: string): number {
textHeight(text: string, size: number, font: string, scale: number = 1): number {
return this._tex
? this.texTextDimensions(text, size).height
: this.textDimensions(text, size, font).height;
? this.texTextDimensions(text, size, scale).height
: this.textDimensions(text, size, font, scale).height;
}

textDimensions(text: string, size: number, font: string): { width: number; height: number; } {
textDimensions(text: string, size: number, font: string, scale: number = 1): { width: number; height: number; } {
if (this._tex) {
return this.texTextDimensions(text, size);
return this.texTextDimensions(text, size, scale);
} else {
const ctx = this._canvas.getContext('2d')!;
ctx.font = `${size}px ${font}`;
ctx.font = `${size * scale}px ${font}`;

const tm = ctx.measureText(text);

Expand All @@ -48,7 +48,7 @@ class TextMeasurement {
return { minX: 0, maxX: 0, minY: 0, maxY: 0, width: 0, height: 0 };
} else {
const ctx = this._canvas.getContext('2d')!;
ctx.font = `${text.fontSize()}px ${text.fontFamily()}`;
ctx.font = `${text.fontSize() * text.currentScale()}px ${text.fontFamily()}`;
ctx.textAlign = text.align();
ctx.textBaseline = text.baseline();

Expand All @@ -63,37 +63,25 @@ class TextMeasurement {

return { minX, maxX, minY, maxY, height: maxY - minY, width: tm.width };
}

// const { x, y } = text;
// const { width, height } = text.size;
// const { width: textWidth, height: textHeight } = text.size;
// const { baseline } = text;

// const minX = x;
// const maxX = x + width;
// const minY = y - height;
// const maxY = y;

// return { minX, maxX, minY, maxY };
}

textDimensionWithCss(text: string, size: number, font: string) {
const div = document.createElement('div');
div.style.font = `${size}px ${font}`;
div.style.position = 'absolute';
div.style.visibility = 'hidden';
// div.style.height = 'auto';
// div.style.width = 'auto';
div.style.whiteSpace = 'nowrap';
div.textContent = text;
document.body.appendChild(div);
const height = div.clientHeight;
const width = div.clientWidth;
// const cssInfo = window.getComputedStyle(div, null);

document.body.removeChild(div);
return { width, height };
}
// textDimensionWithCss(text: string, size: number, font: string) {
// const div = document.createElement('div');
// div.style.font = `${size}px ${font}`;
// div.style.position = 'absolute';
// div.style.visibility = 'hidden';
// // div.style.height = 'auto';
// // div.style.width = 'auto';
// div.style.whiteSpace = 'nowrap';
// div.textContent = text;
// document.body.appendChild(div);
// const height = div.clientHeight;
// const width = div.clientWidth;
// // const cssInfo = window.getComputedStyle(div, null);

// document.body.removeChild(div);
// return { width, height };
// }

private texTextDimensions(text: string, size: number, scale: number = 1): { width: number; height: number; } {
const { width, height } = TexGenerator.getImage({ text, size, scale });
Expand Down
2 changes: 1 addition & 1 deletion src/tex_generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class TexGenerator {
const height = parseFloat(adaptor.getAttribute(svgNode, 'height'));

const pixelsPerEx = 2;
const scaleFactor = size / pixelsPerEx;
const scaleFactor = size * scale / pixelsPerEx;

const adjustedWidth = width * scaleFactor;
const adjustedHeight = height * scaleFactor;
Expand Down
129 changes: 70 additions & 59 deletions test/script_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class TestAnimation extends BaseAnimation {

class TestScene extends Scene {
compose() {
this.add(new GridLines());
// this.add(new GridLines());

// const a = this.add(new Axes({
// xRange: [-1, 5],
Expand Down Expand Up @@ -432,68 +432,79 @@ class TestScene extends Scene {
// l.changeendpoints(d1, d2);
// }, { duration: 3000, easing: easing.linear, repeat: true, yoyo: true, }));

const axes = new Axes({
xLength: 8,
yLength: 6,
xRange: [0, 1],
xStep: 0.25,
yRange: [0, 35],
yStep: 5,
showLabels: false,
showTicks: false,
xLabel: 'time (h)',
yLabel: 'distance (miles)'
});
// const axes = new Axes({
// xLength: 8,
// yLength: 6,
// xRange: [0, 1],
// xStep: 0.25,
// yRange: [0, 35],
// yStep: 5,
// showLabels: false,
// showTicks: false,
// xLabel: 'time (h)',
// yLabel: 'distance (miles)'
// });

const fn = x => Math.pow(Math.E, 12 * x * x - 2.5);
const plot = axes.plot(fn).changeLineColor(Colors.green());

const p = axes.point([0.5, fn(0.5)]);
const q = axes.point([0.69, fn(0.69)]);

const pText = new Tex({ text: 'P' }).nextTo(p, [-1, 1]);
const qText = new Tex({ text: 'Q' }).nextTo(q, RIGHT(1.5));
const qDot = new Dot({ center: q, color: Colors.blue() });
const secant = new Line({ from: p, to: q, length: 6, lineColor: Colors.blue() });
const tangent = new TangentLine({ plot, x: 0.5, length: 4, color: Colors.pink() });
const tangentText = new Tex(`m`).changeColor(Colors.pink()).nextTo(tangent.to());
const secantText = new Tex(`m_{\sec} = `).nextTo(secant.to()).changeColor(Colors.blue());
const secantSlope = new Text('').changeColor(Colors.blue()).nextTo(secantText, RIGHT());
// const secantText = new Text(`m_{\\sec}`).nextTo(secant.to()).changeColor(Colors.blue());

this.add(
axes, plot,
// const fn = x => Math.pow(Math.E, 12 * x * x - 2.5);
// const plot = axes.plot(fn).changeLineColor(Colors.green());

// const p = axes.point([0.5, fn(0.5)]);
// const q = axes.point([0.69, fn(0.69)]);

// const pText = new Tex({ text: 'P' }).nextTo(p, [-1, 1]);
// const qText = new Tex({ text: 'Q' }).nextTo(q, RIGHT(1.5));
// const qDot = new Dot({ center: q, color: Colors.blue() });
// const secant = new Line({ from: p, to: q, length: 6, lineColor: Colors.blue() });
// const tangent = new TangentLine({ plot, x: 0.5, length: 4, color: Colors.pink() });
// const tangentText = new Tex(`m`).changeColor(Colors.pink()).nextTo(tangent.to());
// const secantText = new Tex(`m_{\sec} = `).nextTo(secant.to()).changeColor(Colors.blue());
// const secantSlope = new Text('').changeColor(Colors.blue()).nextTo(secantText, RIGHT());
// // const secantText = new Text(`m_{\\sec}`).nextTo(secant.to()).changeColor(Colors.blue());

// this.add(
// axes, plot,

secant,
tangent,
new Dot({ center: p, color: Colors.pink() }),
// secant,
// tangent,
// new Dot({ center: p, color: Colors.pink() }),

qDot,
pText, qText,
tangentText,
secantText,
secantSlope,
);

// Need to throttle how quickly tex is updated since an image is being generated and rendered
const updateText = utils.throttle(text => secantText.changeText(text), 200);

this.add(new Updater((pctComplete: number, starting: boolean) => {
const rx = math.lerp(0.5, 0.75, 1 - pctComplete);
const m = (25 * rx * rx - 25 * 0.5 * 0.5) / (rx - 0.5);

const x = math.lerp(0.51, 0.69, 1 - pctComplete);
qDot.moveTo(plot.pointAtX(x));
secant.changeEndpoints(p, qDot, true);
qText.nextTo(qDot.center(), RIGHT(), 0.3);
secantText.nextTo(secant.to()) //.changeText(`m_{\\sec} = ${m.toFixed(2)}`);

// console.log(m, pctComplete)
// updateText(`m_{\\sec} = ${m.toFixed(2)}`);
secantSlope.nextTo(secantText, RIGHT()).changeText(`${m.toFixed(2)}`);

}, { duration: 5000, easing: Easing.linear, repeat: true, yoyo: true, }));
// qDot,
// pText, qText,
// tangentText,
// secantText,
// secantSlope,
// );

// // Need to throttle how quickly tex is updated since an image is being generated and rendered
// const updateText = utils.throttle(text => secantText.changeText(text), 200);

// this.add(new Updater((pctComplete: number, starting: boolean) => {
// const rx = math.lerp(0.5, 0.75, 1 - pctComplete);
// const m = (25 * rx * rx - 25 * 0.5 * 0.5) / (rx - 0.5);

// const x = math.lerp(0.51, 0.69, 1 - pctComplete);
// qDot.moveTo(plot.pointAtX(x));
// secant.changeEndpoints(p, qDot, true);
// qText.nextTo(qDot.center(), RIGHT(), 0.3);
// secantText.nextTo(secant.to()) //.changeText(`m_{\\sec} = ${m.toFixed(2)}`);

// // console.log(m, pctComplete)
// // updateText(`m_{\\sec} = ${m.toFixed(2)}`);
// secantSlope.nextTo(secantText, RIGHT()).changeText(`${m.toFixed(2)}`);

// }, { duration: 5000, easing: Easing.linear, repeat: true, yoyo: true, }));
// }, { duration: 5000, easing: x => Easing.easeStep(x, 10), repeat: true, yoyo: true, }));

const v = new Tex({ text: '\\mathbb{V}', color: '#343434' }).scale(7);
v.shift(LEFT(2.25), UP(1.5));

const circle = new Circle({ color: '#87c2a5', lineColor: Colors.transparent() }).shift(LEFT());
const square = new Square({ color: '#525893', lineColor: Colors.transparent() }).shift(UP());
const triangle = new Triangle({ color: '#e07a5f', lineColor: Colors.transparent() }).shift(RIGHT());

const logo = new Group(triangle, square, circle, v);
logo.moveTo(ORIGIN);
this.add(logo);
}
}

Expand Down
Loading

0 comments on commit aad88c4

Please sign in to comment.