Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 134 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

34 changes: 34 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"name": "ebox",
"version": "1.0.0",
"description": "Create rectangles within After Effects expressions ",
"main": "src/index.ts",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"tsc": "tsc",
"build": "rollup -c",
"watch": "rollup -cw",
"release": "hub release create -a 'dist/eKeys.jsx"
},
"repository": {
"type": "git",
"url": "git+https://github.com/motiondeveloper/ebox.git"
},
"author": "Tim Haywood",
"license": "MIT",
"bugs": {
"url": "https://github.com/motiondeveloper/ebox/issues"
},
"homepage": "https://github.com/motiondeveloper/ebox#readme",
"devDependencies": {
"@rollup/plugin-typescript": "^5.0.2",
"prettier": "^1.16.4",
"rollup": "^2.26.3",
"rollup-plugin-ae-jsx": "^1.1.3",
"tslib": "^2.0.1",
"typescript": "^3.9.7"
},
"dependencies": {
"expression-globals-typescript": "^1.1.1"
}
}
4 changes: 4 additions & 0 deletions prettier.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module.exports = {
trailingComma: "es5",
singleQuote: true,
};
21 changes: 21 additions & 0 deletions rollup.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import typescript from '@rollup/plugin-typescript';
import afterEffectJsx from 'rollup-plugin-ae-jsx';

export default {
input: 'src/index.ts',
output: {
file: 'dist/eBox.jsx',
format: 'cjs',
},
plugins: [
typescript({
module: 'esnext',
target: 'esnext',
noImplicitAny: true,
moduleResolution: 'node',
strict: true,
lib: ['esnext'],
}),
afterEffectJsx(),
],
};
176 changes: 176 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
import {
LayerBase,
PropertyBase,
Vector2D,
Points,
} from 'expression-globals-typescript';

// Creating layer and property mocks
const thisLayer = Object.create(LayerBase);
const thisProperty = Object.create(PropertyBase);

// eBox types
type Anchor = 'topLeft' | 'topRight' | 'bottomRight' | 'bottomLeft' | 'center';
interface BoxProps {
size: Vector2D;
position: Vector2D;
anchor: Anchor;
isClosed: boolean;
}

function createBox({
size = [100, 100],
position = [0, 0],
anchor = 'center',
isClosed = true,
}: BoxProps) {
const pointOrder: Anchor[] = [
'topLeft',
'topRight',
'bottomRight',
'bottomLeft',
];

function positionToCenter(
position: Vector2D,
size: Vector2D,
anchor: Anchor
): Vector2D {
const positionCalculations = {
center: (): Vector2D => position,
topLeft: (): Vector2D => [
position[0] + size[0] / 2,
position[1] + size[1] / 2,
],
topRight: (): Vector2D => [
position[0] - size[0] / 2,
position[1] + size[1] / 2,
],
bottomLeft: (): Vector2D => [
position[0] + size[0] / 2,
position[1] - size[1] / 2,
],
bottomRight: (): Vector2D => [
position[0] - size[0] / 2,
position[1] - size[1] / 2,
],
};

return positionCalculations[anchor]();
}

function sizeToPoints(size: Vector2D): Points {
return [
[-size[0] / 2, -size[1] / 2],
[size[0] / 2, -size[1] / 2],
[size[0] / 2, size[1] / 2],
[-size[0] / 2, size[1] / 2],
];
}
function movePoints(
points: Points,
oldPosition: Vector2D,
newPosition: Vector2D
): Points {
const positionDelta: Vector2D = newPosition.map(
(dimension, dimensionIndex): number => {
return dimension - oldPosition[dimensionIndex];
}
) as Vector2D;

return points.map(
(point: Vector2D): Vector2D => {
return point.map((dimension, dimensionIndex) => {
return dimension + positionDelta[dimensionIndex];
}) as Vector2D;
}
) as Points;
}

function pointsToComp(points: Points): Points {
return points.map(
(point): Vector2D => thisLayer.fromCompToSurface(point) as Vector2D
) as Points;
}
function pointsToPath(points: Points, isClosed: boolean) {
return thisProperty.createPath(points, [], [], isClosed);
}

const centerPosition = positionToCenter(position, size, anchor);
interface OutputBox extends BoxProps {
centerPosition: Vector2D;
}
let boxPoints: Points = createPointsFromBoxProps({
size,
position,
anchor,
isClosed,
centerPosition,
});

function getBoxPath() {
return pointsToPath(boxPoints, isClosed);
}
function createPointsFromBoxProps(boxProps: OutputBox): Points {
const points = sizeToPoints(boxProps.size);
const centeredPoints = movePoints(points, [0, 0], boxProps.centerPosition);
const compPositionPoints = pointsToComp(centeredPoints);

return compPositionPoints;
}

function scalePoints(scale: Vector2D = [100, 100], anchor: Anchor): void {
// Remap scale to [0..1]
const normalizedScale: Vector2D = scale.map(
scale => scale / 100
) as Vector2D;

// Get index of anchor point
const anchorPointIndex: number = pointOrder.indexOf(anchor);
const anchorPoint: Vector2D = boxPoints[anchorPointIndex];

// Calculate distance from anchor point
const pointDeltas: Points = boxPoints.map(point => {
return point.map((dimension, dimensionIndex): number => {
return dimension - anchorPoint[dimensionIndex];
}) as Vector2D;
}) as Points;

// Scale the point deltas according to input scale
const scaledPointDeltas: Points = pointDeltas.map(
(point): Vector2D => {
return point.map((dimension, dimensionIndex): number => {
return dimension * normalizedScale[dimensionIndex];
}) as Vector2D;
}
) as Points;

const scaledPoints: Points = boxPoints.map(
(point, pointIndex): Vector2D => {
if (pointIndex !== anchorPointIndex) {
// If not the anchor point
// Create the point from the scaledPointDelta
return point.map((pointDimension, dimensionIndex): number => {
return (
anchorPoint[dimensionIndex] +
scaledPointDeltas[pointIndex][dimensionIndex]
);
}) as Vector2D;
} else {
// If the anchor point
// Return as is
return point;
}
}
) as Points;

boxPoints = scaledPoints;
}

return {
setScale: scalePoints,
getPath: getBoxPath,
};
}

export { createBox };