@teachinglab/omd
Version:
omd
513 lines (397 loc) • 15.8 kB
JavaScript
import { omdColor } from "./omdColor.js";
import { jsvgGroup, jsvgPath, jsvgLine, jsvgTextLine, jsvgEllipse } from "@teachinglab/jsvg";
export class omdRightTriangle extends jsvgGroup
{
constructor()
{
// initialization
super();
this.type = "omdRightTriangle";
this.horizontalLeg = 5;
this.verticalLeg = 10;
this.angleA = 0;
this.angleB = 0;
this.hypotenuse = 0;
this.unitScale = 10;
this.showLabels = false;
this.shapePath = new jsvgPath();
this.shapePath.setStrokeColor( "black" );
this.shapePath.setStrokeWidth( 2 );
this.shapePath.setFillColor( omdColor.lightGray );
this.addChild( this.shapePath );
this.labelsHolder = new jsvgGroup();
this.addChild( this.labelsHolder );
this.updateLayout();
}
loadFromJSON( data )
{
if ( typeof data.horizontalLeg != "undefined" )
this.horizontalLeg = data.horizontalLeg;
if ( typeof data.verticalLeg != "undefined" )
this.verticalLeg = data.verticalLeg;
if ( typeof data.angleA != "undefined" )
this.angleA = data.angleA;
if ( typeof data.angleB != "undefined" )
this.angleB = data.angleB;
if ( typeof data.hypotenuse != "undefined" )
this.hypotenuse = data.hypotenuse;
if ( typeof data.unitScale != "undefined" )
this.unitScale = data.unitScale;
if ( typeof data.showLabels != "undefined" )
this.showLabels = data.showLabels;
// calculate leg lengths
if ( this.angleA && this.hypotenuse )
{
var A = Math.PI * this.angleA / 180.0;
this.horizontalLeg = Math.cos(A) * this.hypotenuse;
this.verticalLeg = Math.sin(A) * this.hypotenuse;
}
this.updateLayout();
}
updateLayout()
{
// Center the triangle within the viewBox
const offsetX = 10; // Left margin
const offsetY = this.unitScale * this.verticalLeg + 10; // Bottom margin
this.shapePath.clearPoints();
this.shapePath.addPoint(offsetX, offsetY);
this.shapePath.addPoint(offsetX + this.unitScale * this.horizontalLeg, offsetY);
this.shapePath.addPoint(offsetX + this.unitScale * this.horizontalLeg, offsetY - this.unitScale * this.verticalLeg);
this.shapePath.addPoint(offsetX, offsetY);
this.shapePath.updatePath();
if ( this.showLabels )
{
this.labelsHolder.removeAllChildren();
var L = new omdShapeLabelSet();
var A = this.horizontalLeg.toString();
var B = this.verticalLeg.toString();
var C = "";
L.initializeWithShapePath( this.shapePath, [A,B,C] );
this.labelsHolder.addChild( L );
}
// Set viewBox to center the shape
this.width = this.unitScale * this.horizontalLeg + 20;
this.height = this.unitScale * this.verticalLeg + 20;
this.svgObject.setAttribute('viewBox', `0 0 ${this.width} ${this.height}`);
}
}
export class omdIsoscelesTriangle extends jsvgGroup
{
constructor()
{
// initialization
super();
this.type = "omdIsoscelesTriangle";
this.triangleBase = 5;
this.triangleHeight = 10;
this.unitScale = 10;
this.showLabels = false;
this.shapePath = new jsvgPath();
this.shapePath.setStrokeColor( "black" );
this.shapePath.setStrokeWidth( 2 );
this.shapePath.setFillColor( omdColor.lightGray );
this.addChild( this.shapePath );
this.labelsHolder = new jsvgGroup();
this.addChild( this.labelsHolder );
this.updateLayout();
}
loadFromJSON( data )
{
if ( typeof data.base != "undefined" )
this.triangleBase = data.base;
if ( typeof data.height != "undefined" )
this.triangleHeight = data.height;
if ( typeof data.unitScale != "undefined" )
this.unitScale = data.unitScale;
if ( typeof data.showLabels != "undefined" )
this.showLabels = data.showLabels;
this.updateLayout();
}
updateLayout()
{
this.shapePath.clearPoints();
this.shapePath.addPoint( -0.5*this.unitScale*this.triangleBase, 0 );
this.shapePath.addPoint( 0.5*this.unitScale*this.triangleBase, 0 );
this.shapePath.addPoint( 0, -1.0*this.unitScale*this.triangleHeight );
this.shapePath.addPoint( -0.5*this.unitScale*this.triangleBase, 0 );
this.shapePath.updatePath();
if ( this.showLabels )
{
this.labelsHolder.removeAllChildren();
var L = new omdShapeLabelSet();
L.initializeWithShapePath( this.shapePath );
this.labelsHolder.addChild( L );
}
// Set dimensions and viewBox for API compatibility
this.width = this.unitScale * this.triangleBase + 20;
this.height = this.unitScale * this.triangleHeight + 20;
this.svgObject.setAttribute('viewBox', `0 0 ${this.width} ${this.height}`);
}
}
export class omdRectangle extends jsvgGroup
{
constructor()
{
// initialization
super();
this.type = "omdRectangle";
this.rectWidth = 10;
this.rectHeight = 10;
this.unitScale = 10;
this.showLabels = false;
this.shapePath = new jsvgPath();
this.shapePath.setStrokeColor( "black" );
this.shapePath.setStrokeWidth( 2 );
this.shapePath.setFillColor( omdColor.lightGray );
this.addChild( this.shapePath );
this.labelsHolder = new jsvgGroup();
this.addChild( this.labelsHolder );
this.updateLayout();
}
loadFromJSON( data )
{
if ( typeof data.width != "undefined" )
this.rectWidth = data.width;
if ( typeof data.height != "undefined" )
this.rectHeight = data.height;
if ( typeof data.unitScale != "undefined" )
this.unitScale = data.unitScale;
if ( typeof data.showLabels != "undefined" )
this.showLabels = data.showLabels;
this.updateLayout();
}
updateLayout()
{
// Center the rectangle within the viewBox
const offsetX = 10;
const offsetY = this.unitScale * this.rectHeight + 10;
this.shapePath.clearPoints();
this.shapePath.addPoint(offsetX, offsetY);
this.shapePath.addPoint(offsetX + this.unitScale * this.rectWidth, offsetY);
this.shapePath.addPoint(offsetX + this.unitScale * this.rectWidth, offsetY - this.unitScale * this.rectHeight);
this.shapePath.addPoint(offsetX, offsetY - this.unitScale * this.rectHeight);
this.shapePath.addPoint(offsetX, offsetY);
this.shapePath.updatePath();
if ( this.showLabels )
{
this.labelsHolder.removeAllChildren();
var L = new omdShapeLabelSet();
var A = this.rectWidth.toString();
var B = this.rectHeight.toString();
L.initializeWithShapePath( this.shapePath, [A,B,"",""] );
this.labelsHolder.addChild( L );
}
// Set viewBox
this.width = this.unitScale * this.rectWidth + 20;
this.height = this.unitScale * this.rectHeight + 20;
this.svgObject.setAttribute('viewBox', `0 0 ${this.width} ${this.height}`);
}
}
export class omdEllipse extends jsvgGroup
{
constructor()
{
// initialization
super();
this.type = "omdEllipse";
this.rectWidth = 10;
this.rectHeight = 5;
this.unitScale = 10;
this.shapePath = new jsvgEllipse();
this.shapePath.setWidthAndHeight( this.rectWidth*this.unitScale, this.rectHeight*this.unitScale );
this.shapePath.setStrokeColor( "black" );
this.shapePath.setStrokeWidth( 2 );
this.shapePath.setFillColor( omdColor.lightGray );
this.addChild( this.shapePath );
this.updateLayout();
}
loadFromJSON( data )
{
if ( typeof data.width != "undefined" )
this.rectWidth = data.width;
if ( typeof data.height != "undefined" )
this.rectHeight = data.height;
if ( typeof data.unitScale != "undefined" )
this.unitScale = data.unitScale;
this.updateLayout();
}
updateLayout()
{
this.shapePath.setWidthAndHeight( this.rectWidth*this.unitScale, this.rectHeight*this.unitScale );
// Set dimensions and viewBox for API compatibility
this.width = this.rectWidth * this.unitScale + 20;
this.height = this.rectHeight * this.unitScale + 20;
this.svgObject.setAttribute('viewBox', `0 0 ${this.width} ${this.height}`);
}
}
export class omdCircle extends jsvgGroup
{
constructor()
{
// initialization
super();
this.type = "omdCircle";
this.radius = 5;
this.unitScale = 10;
this.shapePath = new jsvgEllipse();
this.shapePath.setWidthAndHeight( this.radius*this.unitScale, this.radius*this.unitScale );
this.shapePath.setStrokeColor( "black" );
this.shapePath.setStrokeWidth( 2 );
this.shapePath.setFillColor( omdColor.lightGray );
this.addChild( this.shapePath );
this.updateLayout();
}
loadFromJSON( data )
{
if ( typeof data.radius != "undefined" )
this.radius = data.radius;
if ( typeof data.unitScale != "undefined" )
this.unitScale = data.unitScale;
this.updateLayout();
}
updateLayout()
{
this.shapePath.setWidthAndHeight( 2.0*this.radius*this.unitScale, 2.0*this.radius*this.unitScale );
// Set dimensions and viewBox for API compatibility
this.width = 2.0 * this.radius * this.unitScale + 20;
this.height = 2.0 * this.radius * this.unitScale + 20;
this.svgObject.setAttribute('viewBox', `0 0 ${this.width} ${this.height}`);
}
}
export class omdRegularPolygon extends jsvgGroup
{
constructor()
{
// initialization
super();
this.type = "omdRegularPolygon";
this.radius = 5;
this.numberOfSides = 5;
this.unitScale = 10;
this.showLabels = false;
this.shapePath = new jsvgPath();
this.shapePath.setWidthAndHeight( this.radius*this.unitScale, this.radius*this.unitScale );
this.shapePath.setStrokeColor( "black" );
this.shapePath.setStrokeWidth( 2 );
this.shapePath.setFillColor( omdColor.lightGray );
this.addChild( this.shapePath );
this.labelsHolder = new jsvgGroup();
this.addChild( this.labelsHolder );
this.updateLayout();
}
loadFromJSON( data )
{
if ( typeof data.radius != "undefined" )
this.radius = data.radius;
if ( typeof data.numberOfSides != "undefined" )
this.numberOfSides = data.numberOfSides;
if ( typeof data.unitScale != "undefined" )
this.unitScale = data.unitScale;
if ( typeof data.showLabels != "undefined" )
this.showLabels = data.showLabels;
this.updateLayout();
}
updateLayout()
{
this.shapePath.clearPoints();
var angleOffset = 0;
if ( this.numberOfSides % 2 == 1 )
{
angleOffset = Math.PI / this.numberOfSides * 0.5; // shift angle for odd number of sides
if ( ((this.numberOfSides-1)/2) % 2 == 0 )
angleOffset *= -1.0;
}
for( var i=0; i<=this.numberOfSides; i++ )
{
var A = -2.0 * Math.PI / this.numberOfSides * i;
A += angleOffset;
var pX = Math.cos(A) * this.radius * this.unitScale;
var pY = Math.sin(A) * this.radius * this.unitScale;
this.shapePath.addPoint( pX, pY );
}
this.shapePath.updatePath();
if ( this.showLabels )
{
// make label
this.labelsHolder.removeAllChildren();
var L = new omdShapeLabelSet();
var sideLength = 2.0 * this.radius * Math.sin( Math.PI / this.numberOfSides );
var A = sideLength.toFixed(2);
L.initializeWithShapePath( this.shapePath, [ A ] );
this.labelsHolder.addChild( L );
}
// Set dimensions and viewBox for API compatibility
this.width = 2.0 * this.radius * this.unitScale + 20;
this.height = 2.0 * this.radius * this.unitScale + 20;
this.svgObject.setAttribute('viewBox', `-${this.width/2} -${this.height/2} ${this.width} ${this.height}`);
}
}
export class omdShapeLabelSet extends jsvgGroup
{
constructor()
{
// initialization
super();
}
initializeWithShapePath( shapePath, labelTextArray=[] )
{
// this assumes counterclockwise path
// create lines
for( var i=0; i<shapePath.points.length-1; i++ )
{
if ( i >= labelTextArray.length || labelTextArray[i].length == 0 )
continue;
// get points
var point0 = shapePath.points[i];
var x0 = point0.x;
var y0 = point0.y;
var point1 = shapePath.points[i+1];
var x1 = point1.x;
var y1 = point1.y;
// get normalized vector
var dX = x1 - x0;
var dY = y1 - y0;
var L = Math.sqrt( dX*dX + dY*dY );
dX /= L;
dY /= L;
// get normal
var normalX = -1.0 * dY;
var normalY = dX;
// make line
var labelLine = new jsvgLine();
var newLineX0 = x0 + normalX * 10.0;
var newLineY0 = y0 + normalY * 10.0;
var newLineX1 = x1 + normalX * 10.0;
var newLineY1 = y1 + normalY * 10.0;
labelLine.setEndpoints( newLineX0, newLineY0, newLineX1, newLineY1 );
this.addChild( labelLine );
// center point
var centerX = (x0 + x1) / 2.0;
var centerY = (y0 + y1) / 2.0;
var angle = Math.atan2( dY, dX );
angle *= 180 / Math.PI;
angle += 180.0;
angle = angle % 360.0;
var originalAngle = angle;
// flip upside-down text
var offset = 0;
if ( angle > 90 && angle < 270 )
{
angle -= 180.0;
offset += 10.0;
}
// label text
var textCenterX = centerX + normalX * (15.0+offset);
var textCenterY = centerY + normalY * (15.0+offset);
var labelText = new jsvgTextLine();
labelText.setAlignment("center");
labelText.setFontFamily( "Albert Sans" );
labelText.setFontColor( "black" );
labelText.setFontSize( 12 );
labelText.setPosition( textCenterX, textCenterY );
labelText.setRotation( angle );
this.addChild( labelText );
labelText.setText( labelTextArray[i] );
}
}
}