div.js 4.95 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} beggining 
	 * @param {Vector} end 
	 */
	constructor(beggining, end) {
		this.beggining = beggining;
		this.end = end;
	}

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

class Text {

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

	/**
	 * @param {jsPDF} pdf
	 */
	draw(pdf) {
		pdf.text(
			this.text,
			this.position.x,
			this.position.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) {
	pdf.setFont("Courier");
	pdf.setFontType("normal");
	pdf.setFontSize(10);
	tree.children.forEach(child => {
		switch (child.constructor.name) {
			case "Text":
				pdf.text(child.text, child.position.x, child.position.y);
				break;
			case "Div":
				if (child.drawOutline) {
					child.drawDivBox(pdf);
				}
				compile(child, pdf);
				break;
		}
	});
	return pdf;
}