function( { runningDimensions, topLeftCorner, bottomRightCorner }, entity: PvjsonSingleFreeNode | PvjsonEdge ) { const { zIndex } = entity; const zIndexIsFinite = isFinite(zIndex); const runningDimensionsZIndexIsFinite = isFinite( runningDimensions.zIndex ); if (zIndexIsFinite && runningDimensionsZIndexIsFinite) { runningDimensions.zIndex = Math.min(zIndex, runningDimensions.zIndex); } else if (zIndexIsFinite) { runningDimensions.zIndex = zIndex; } if (isPvjsonEdge(entity)) { const points = entity.points; // If entity is an edge const firstPoint = points[0]; const firstPointX = firstPoint.x; const firstPointY = firstPoint.y; const lastPoint = points[points.length - 1]; const lastPointX = lastPoint.x; const lastPointY = lastPoint.y; topLeftCorner.x = Math.min(topLeftCorner.x, firstPointX, lastPointX); topLeftCorner.y = Math.min(topLeftCorner.y, firstPointY, lastPointY); bottomRightCorner.x = Math.max( bottomRightCorner.x, firstPointX, lastPointX ); bottomRightCorner.y = Math.max( bottomRightCorner.y, firstPointY, lastPointY ); } else { // If entity is a node topLeftCorner.x = Math.min(topLeftCorner.x, entity.x); topLeftCorner.y = Math.min(topLeftCorner.y, entity.y); bottomRightCorner.x = Math.max( bottomRightCorner.x, entity.x + entity.width ); bottomRightCorner.y = Math.max( bottomRightCorner.y, entity.y + entity.height ); } runningDimensions.x = topLeftCorner.x - padding - strokeWidth; runningDimensions.y = topLeftCorner.y - padding - strokeWidth; runningDimensions.width = bottomRightCorner.x - topLeftCorner.x + 2 * (padding + strokeWidth); runningDimensions.height = bottomRightCorner.y - topLeftCorner.y + 2 * (padding + strokeWidth); return { runningDimensions, topLeftCorner, bottomRightCorner }; },
/** * getOffsetAndOrientationScalarsAlongAxis * * @param relValue {number} * @param axis {string} * @param referencedEntity * @return {OffsetOrientationAndPositionScalarsAlongAxis} */ function getOffsetAndOrientationScalarsAlongAxis( positionScalar: number, relativeOffsetScalar: number, axis: "x" | "y", // TODO are we correctly handling the case of a group as the referenced // entity? Do we have the group width and height yet to properly calculate // this? referencedEntity: PvjsonNode ): OffsetOrientationAndPositionScalarsAlongAxis { let offsetScalar = relativeOffsetScalar * (axis === "x" ? referencedEntity.width : referencedEntity.height); // TODO WP536 has a referenced entity that lacks width/height. Why? // The referenced entity was a group. // Is the problem that the group was nested? // Or is it a problem with the order of evaluation of entities (trying to // parse a dependent entity before its dependencies were parsed)? if (!isFinite(offsetScalar)) { throw new Error( ` Got non-finite value ${offsetScalar} for offsetScalar along ${axis} axis for getOffsetAndOrientationScalarsAlongAxis( positionScalar=${positionScalar}, relativeOffsetScalar=${relativeOffsetScalar}, referencedEntity= ${JSON.stringify(referencedEntity, null, " ")} ) ` ); } // orientationScalar here refers to the initial direction the edge takes as // it moves away from the entity to which it is attached. let orientationScalar; if (positionScalar === 0) { orientationScalar = -1; } else if (positionScalar === 1) { orientationScalar = 1; } else { orientationScalar = 0; } return { offsetScalar, orientationScalar, positionScalar }; }
.filter(dims => !isFinite(dims[1]));
export function getGroupDimensions( padding: number, strokeWidth: number, containedEntities: PvjsonEntity[] ): NodeDimensions { if (containedEntities.length === 0) { console.warn(`Warning: Empty group observed.`); return { x: 0, y: 0, width: 0, height: 0, zIndex: 0 }; } else if (!isFinite(padding)) { throw new Error("Invalid padding value: ${padding}"); } else if (!isFinite(strokeWidth)) { throw new Error("Invalid strokeWidth value: ${strokeWidth}"); } const dimensions = containedEntities .filter(entity => isPvjsonSingleFreeNode(entity) || isPvjsonEdge(entity)) .reduce( function( { runningDimensions, topLeftCorner, bottomRightCorner }, entity: PvjsonSingleFreeNode | PvjsonEdge ) { const { zIndex } = entity; const zIndexIsFinite = isFinite(zIndex); const runningDimensionsZIndexIsFinite = isFinite( runningDimensions.zIndex ); if (zIndexIsFinite && runningDimensionsZIndexIsFinite) { runningDimensions.zIndex = Math.min(zIndex, runningDimensions.zIndex); } else if (zIndexIsFinite) { runningDimensions.zIndex = zIndex; } if (isPvjsonEdge(entity)) { const points = entity.points; // If entity is an edge const firstPoint = points[0]; const firstPointX = firstPoint.x; const firstPointY = firstPoint.y; const lastPoint = points[points.length - 1]; const lastPointX = lastPoint.x; const lastPointY = lastPoint.y; topLeftCorner.x = Math.min(topLeftCorner.x, firstPointX, lastPointX); topLeftCorner.y = Math.min(topLeftCorner.y, firstPointY, lastPointY); bottomRightCorner.x = Math.max( bottomRightCorner.x, firstPointX, lastPointX ); bottomRightCorner.y = Math.max( bottomRightCorner.y, firstPointY, lastPointY ); } else { // If entity is a node topLeftCorner.x = Math.min(topLeftCorner.x, entity.x); topLeftCorner.y = Math.min(topLeftCorner.y, entity.y); bottomRightCorner.x = Math.max( bottomRightCorner.x, entity.x + entity.width ); bottomRightCorner.y = Math.max( bottomRightCorner.y, entity.y + entity.height ); } runningDimensions.x = topLeftCorner.x - padding - strokeWidth; runningDimensions.y = topLeftCorner.y - padding - strokeWidth; runningDimensions.width = bottomRightCorner.x - topLeftCorner.x + 2 * (padding + strokeWidth); runningDimensions.height = bottomRightCorner.y - topLeftCorner.y + 2 * (padding + strokeWidth); return { runningDimensions, topLeftCorner, bottomRightCorner }; }, { topLeftCorner: { x: Infinity, y: Infinity }, bottomRightCorner: { x: 0, y: 0 }, runningDimensions: { zIndex: Infinity } } as { topLeftCorner: Corner; bottomRightCorner: Corner; runningDimensions: NodeDimensions; } ).runningDimensions; const propertiesToCheck = ["x", "y", "width", "height", "zIndex"]; const nonFinites = propertiesToCheck .map(function(key) { return [[key], dimensions[key]]; }) .filter(dims => !isFinite(dims[1])); if (nonFinites.length > 0) { throw new Error( `Got a non-finite value(s): ${JSON.stringify(fromPairs(nonFinites), null, " ")} when calling getGroupDimensions( padding: ${padding}, strokeWidth: ${strokeWidth}, containedEntities: ${JSON.stringify(containedEntities, null, " ")} ) ` ); } return dimensions; }
export function validateOrientation(orientation: Orientation): boolean { return !!orientation && isFinite(orientation[0]) && isFinite(orientation[1]); }