@teachinglab/omd
Version:
omd
200 lines (167 loc) • 6.96 kB
JavaScript
import { omdColor } from "./omdColor.js";
import { jsvgGroup, jsvgLine, jsvgEllipse, jsvgRect, jsvgTextBox } from "@teachinglab/jsvg";
export class omdBalanceHanger extends jsvgGroup
{
constructor()
{
// initialization
super();
this.type = "omdBalanceHanger";
this.leftValues = [];
this.rightValues = [];
this.tilt = "none";
this.fontFamily = "Albert Sans";
this.fontSize = 18;
// Background customization properties
this.backgroundColor = omdColor.lightGray;
this.backgroundCornerRadius = 5;
this.backgroundOpacity = 1.0;
this.showBackground = true;
this.updateLayout();
}
loadFromJSON( data )
{
if ( typeof data.leftValues != "undefined" )
this.leftValues = data.leftValues;
if ( typeof data.rightValues != "undefined" )
this.rightValues = data.rightValues;
if ( typeof data.tilt != "undefined" )
this.tilt = data.tilt;
if ( typeof data.fontFamily != "undefined" )
this.fontFamily = data.fontFamily;
if ( typeof data.fontSize != "undefined" )
this.fontSize = data.fontSize;
// Load background customization properties
if ( typeof data.backgroundColor != "undefined" )
this.backgroundColor = data.backgroundColor;
if ( typeof data.backgroundCornerRadius != "undefined" )
this.backgroundCornerRadius = data.backgroundCornerRadius;
if ( typeof data.backgroundOpacity != "undefined" )
this.backgroundOpacity = data.backgroundOpacity;
if ( typeof data.showBackground != "undefined" )
this.showBackground = data.showBackground;
this.updateLayout();
}
setLeftAndRightValues( leftValues, rightValues )
{
this.leftValues = leftValues;
this.rightValues = rightValues;
}
updateLayout()
{
this.removeAllChildren();
// Calculate actual content dimensions
var maxValues = Math.max(this.leftValues.length, this.rightValues.length);
var bX = 50; // Half beam width
var bY = 0;
if ( this.tilt == "left" )
bY = -10;
if ( this.tilt == "right" )
bY = 10;
var slackOnTop = 20;
var valueStackHeight = maxValues > 0 ? (maxValues - 1) * 40 + 30 : 0; // Height of value stacks
// Calculate the actual content bounds
var contentWidth = bX * 2; // Total beam width
var contentHeight = Math.abs(bY) + slackOnTop + valueStackHeight;
// make line (centered at origin)
this.line = new jsvgLine();
this.line.setStrokeColor( "black" );
this.line.setStrokeWidth( 1 );
this.line.setEndpoints( -bX, bY, bX, -bY );
this.addChild( this.line );
// left line
var leftLine = new jsvgLine();
leftLine.setStrokeColor( "black" );
leftLine.setStrokeWidth( 1 );
leftLine.setEndpoints( -bX, bY, -bX, bY + slackOnTop + 40*(this.leftValues.length-1) );
this.addChild( leftLine );
// right line
var right = new jsvgLine();
right.setStrokeColor( "black" );
right.setStrokeWidth( 1 );
right.setEndpoints( bX, -bY, bX, -bY + slackOnTop + 40*(this.rightValues.length-1) );
this.addChild( right );
// center dot (centered at origin)
var dot = new jsvgEllipse();
dot.setWidthAndHeight( 10, 10 );
dot.setFillColor( "black" );
dot.setPosition( 0, 0 ); // Ellipse is centered at its transform; origin aligns with beam midpoint
this.addChild( dot );
this.makeValueStack( this.leftValues, -bX, bY + slackOnTop );
this.makeValueStack( this.rightValues, bX, -bY + slackOnTop );
// Choose a comfortable display size and center the view on origin
const padding = 40;
const displayWidth = Math.max(300, contentWidth + padding);
const displayHeight = Math.max(200, contentHeight + padding);
this.setWidthAndHeight(displayWidth, displayHeight);
this.svgObject.setAttribute("viewBox", `${-displayWidth/2} ${-displayHeight/2} ${displayWidth} ${displayHeight}`);
// Don't set position since the viewBox already handles centering
// this.setPosition(-displayWidth / 2, -displayHeight / 2);
}
makeValueStack( values, xOffset, yOffset )
{
// make boxes on each side
for( var i=0; i<values.length; i++ )
{
// get value
var value = values[i];
var W = 30;
if ( typeof value == "string" )
{
W = 20 + value.length*10;
}
else
{
W = 30;
}
// make box
var box;
if ( typeof value == "string" )
{
box = new jsvgEllipse();
box.setWidthAndHeight( W, 30 );
box.setPosition( xOffset-W/2 + W/2, yOffset + i*40 + 15);
}
else
{
box = new jsvgRect();
box.setWidthAndHeight( W, 30 );
box.setCornerRadius(5);
box.setPosition( xOffset-W/2, yOffset + i*40 );
}
// Apply customizable background properties
if (this.showBackground) {
box.setFillColor(this.backgroundColor);
if (this.backgroundOpacity < 1.0) {
box.setOpacity(this.backgroundOpacity);
}
} else {
box.setFillColor("transparent");
}
// Only apply corner radius to rectangular boxes (jsvgRect), not ellipses
if (this.backgroundCornerRadius > 0 && box.setCornerRadius) {
box.setCornerRadius(this.backgroundCornerRadius);
}
this.addChild( box );
// make box text
var boxText = new jsvgTextBox();
boxText.setWidthAndHeight( W,30 );
boxText.setText ( this.name );
boxText.setFontFamily( this.fontFamily );
boxText.setFontColor( "black" );
boxText.setFontSize( this.fontSize );
boxText.setAlignment("center");
boxText.setVerticalCentering();
boxText.setText( value );
boxText.setPosition( xOffset-W/2, yOffset + i*40 );
this.addChild( boxText );
}
}
setFont( fontFamily, fontSize )
{
this.fontFamily = fontFamily;
if ( fontSize )
this.fontSize = fontSize;
this.updateLayout();
}
}