div.js 5.54 KB
/**
 * 2D Vector.
 * Container for x and y coordinates. 
 */

class Vector {
	constructor(x,y) {
		this.x = x;
		this.y = y;
	}

	/**
	 * @param {Vector} vector
	 */
	addv(vector) {
		return new Vector(
			this.x + vector.x,
			this.y + vector.y
		);
	}

	/**
	 * @param {number} x
	 * @param {number} y
	 */
	addxy(x = 0,y = 0) {
		return new Vector(
			this.x + x,
			this.y + y
		);
	}

	/**
	 * @param {number} n
	 */
	mult(n) {
		return new Vector(
			this.x * n,
			this.y * n
		);
	}
}

class Line {

	/**
	 * @param {Vector} position 
	 * @param {Vector} size 
	 */
	constructor(position, size) {
		this.position = position;
		this.size = size;
	}

	/**
	 * @param {jsPDF} pdf
	 */
	draw(pdf) {
		pdf.line(
			this.position.x,
			this.position.y,
			this.size.x,
			this.size.y
		);
	}
}

class Text {

	/**
	 * Both arguments should be real coordinates and not a percentage based on parent element
	 * @param {string} text 
	 * @param {Vector} position
	 * @param {object} font 
	 */
	constructor(text, position, font = null, align = null) {
		this.text = text;
		this.position = position;
		this.font = font
		this.align = align
	}

	/**
	 * @param {jsPDF} pdf
	 */
	draw(pdf) {

		/** replace ? with 0s (temporary) **/
		if (this.text == "?") this.text = "0.000";

		if (this.font){
			pdf.setFont(this.font.font);
			pdf.setFontType(this.font.type);
			pdf.setFontSize(this.font.size);
		}

		if (this.align) {
			pdf.text(
				this.text,
				this.position.x,
				this.position.y,
				this.align
			);
		}
		else {
			pdf.text(
				this.text,
				this.position.x,
				this.position.y
			)
		}
	}
}

class Image {

	/**
	 * Both arguments should be real coordinates and not a percentage based on parent element
	 * @param {string} text 
	 * @param {Vector} position
	 * @param {object} font 
	 */
	constructor(pointer, position, size) {
		this.pointer = pointer;
		this.position = position;
		this.size = size
	}

	/**
	 * @param {jsPDF} pdf
	 */
	draw(pdf) {
		pdf.addImage(
			this.pointer,
			this.position.x,
			this.position.y,
			this.size.x,
			this.size.y
		);
	}
}

/**
 * This class does NOT regulate if its children go out of its cofinements.
 * It does NOT resize or realocate its children to fit, in any way.
 * Its only purpose is to provide percentage based relative coordinates.
 */
class Div {

	/**
	 * @param {Vector} position 
	 * @param {Vector} size 
	 * @param {bool} draw 
	 */
	constructor(position, size, draw = false) {
		this.position = position;
		this.size = size;
		this.drawOutline = draw;
		this.children = [];
	}

	/** Allows any position you want **/
	addAbsoluteChild(child) {
		if ("draw" in child === false)
			console.error("the object passed needs to have a 'draw' method that takes a jsPDF object");
		this.children.push(child);
		return this.children[this.children.length - 1];
	}
	
	/** Allows only positions inside the Div **/
	addRelativeChild(child) {
		if ("draw" in child === false)
			console.error("the object passed needs to have a 'draw' method that takes a jsPDF object");

		if ("position" in child)
			child.position = this._getDrawCoordinates(child.position);
		
		if ("size" in child)
			child.size = this._getSizeCoordinates(child.size);
		
		this.children.push(child);
		return this.children[this.children.length - 1];
	}

	addAbsoluteChildren(children) {
		const pointers = [];
		children.forEach(c => pointers.push(this.addAbsoluteChild(c)));
		return pointers;
	}

	addRelativeChildren(children) {
		const pointers = [];
		children.forEach(c => pointers.push(this.addRelativeChild(c)));
		return pointers;
	}
	
	/** Allows any position you want relative to the position of this Div. Percentage based **/
	getAbsoluteVector(vector) {
		return this.position.addxy(
			(this.size.x * vector.x / 100),
			(this.size.y * vector.y / 100)
		);
	}
	
	/**
	 * Compiles the true position coordinates based on percentage relative to parent element
	 * Assumes the elements grow to the right and down
	 * @param {Vector} position 
	 */
	_getDrawCoordinates(position) {
		if (position.x < 0 || position.x > 100 || position.y < 0 || position.y > 100)
			console.error("position components need to be real numbers in the range [0;100]");

		return this.position.addxy(
			(this.size.x * position.x / 100),
			(this.size.y * position.y / 100)
		);
	}
		
	/**
	 * Compiles the true size based on percentage relative to parent element
	 * Assumes the elements grow to the right and down
	 * @param {Vector?} size
	 */
	_getSizeCoordinates(size) {
		if (size.x < 0 || size.x > 100 || size.y < 0 || size.y > 100)
			console.error("size components need to be real numbers in the range [0;100]");

		return new Vector (
			this.size.x * size.x / 100,
			this.size.y * size.y / 100,
		);
	}

	draw(pdf) {
		/** Draw the confines of the div **/
		if (this.drawOutline) {
			/** Upper **/
			this.addAbsoluteChild(new Line(
				this.position,
				this.position.addxy(this.size.x, 0)
			));
			/** Lower **/
			this.addAbsoluteChild(new Line(
				this.position.addxy(0, this.size.y),
				this.position.addv(this.size)
			));
			/** Left **/
			this.addAbsoluteChild(new Line(
				this.position,
				this.position.addxy(0, this.size.y)
			));
			/** Right **/
			this.addAbsoluteChild(new Line(
				this.position.addxy(this.size.x, 0),
				this.position.addv(this.size)
			));
		}

		/** Recursion **/
		this.children.forEach( child => child.draw(pdf) );
	}
}

const Pos = {
	"beg": 0,
	"beg_margin": 5,
	"quarter": 25,
	"third": 33.33,
	"middle": 50,
	"two_thirds": 66.67,
	"three_quarters": 75,
	"end_margin": 95,
	"end": 100
};

function compile(tree, pdf) {
	tree.children.forEach(child => child.draw(pdf));
	return pdf;
}