From 77b552a310cba013c2d5fa813217b86b9e84f0eb Mon Sep 17 00:00:00 2001 From: robonen Date: Sun, 27 Nov 2022 08:23:17 +0700 Subject: [PATCH] feat(engine): all figures and transformations ready --- src/components/board.vue | 210 +++++++++++++----- src/components/form/range.vue | 4 +- src/composables/useFigure.ts | 20 ++ .../{states.ts => useTransformations.ts} | 27 ++- src/layouts/default.vue | 2 +- src/pages/figure.vue | 40 +++- src/pages/index.vue | 28 ++- 7 files changed, 262 insertions(+), 69 deletions(-) create mode 100644 src/composables/useFigure.ts rename src/composables/{states.ts => useTransformations.ts} (55%) diff --git a/src/components/board.vue b/src/components/board.vue index 6fa0d0a..2b44346 100644 --- a/src/components/board.vue +++ b/src/components/board.vue @@ -21,48 +21,117 @@ onMounted(() => { const centerX = sizeX / 2; const centerY = sizeY / 2; - const figureSizeH = 200; - const figureSizeW = 200; + const figureSize = 200; - const points: Point[] = [ - [0, 0, figureSizeH, 1], - [0, 100, 0, 1], - [95.1, 30.9, 0, 1], - [58.8, -80.9, 0, 1], - [-58.8, -80.9, 0, 1], - [-95.1, 30.9, 0, 1], + const figureList = { + [Figures.CUBE]: { + points: [ + [0, 0, 0, 1], + [0, 0, 200, 1], + [0, 200, 0, 1], + [0, 200, 200, 1], + [200, 0, 0, 1], + [200, 0, 200, 1], + [200, 200, 0, 1], + [200, 200, 200, 1], + ] as Point[], + faces: [ + [0, 1, 3, 2], + [0, 1, 5, 4], + [0, 2, 6, 4], + [1, 3, 7, 5], + [2, 3, 7, 6], + [4, 5, 7, 6], + ], + }, + + [Figures.OCTAHEDRON]: { + points: [ + [0, 0, 100, 1], + [100, 100, 0, 1], + [100, -100, 0, 1], + [-100, -100, 0, 1], + [-100, 100, 0, 1], + [0, 0, -100, 1], + ] as Point[], + faces: [ + [0, 1, 2], + [0, 2, 3], + [0, 3, 4], + [0, 4, 1], + [5, 1, 2], + [5, 2, 3], + [5, 3, 4], + [5, 4, 1], + ], + }, + + [Figures.TRIHEDRAL_PYRAMID]: { + points: [ + [0, 0, 100, 1], + [0, 80, 0, 1], + [86.6, -50, 0, 1], + [-86.6, -50, 0, 1], + ] as Point[], + faces: [ + [0, 1, 2], + [0, 2, 3], + [0, 3, 1], + [1, 2, 3], + ], + }, + + [Figures.SQUARE_PYRAMID]: { + points: [ + [0, 0, figureSize, 1], + [100, 100, 0, 1], + [100, -100, 0, 1], + [-100, -100, 0, 1], + [-100, 100, 0, 1], + ] as Point[], + faces: [ + [0, 1, 2], + [0, 2, 3], + [0, 3, 4], + [0, 4, 1], + [1, 2, 3, 4], + ], + }, + + [Figures.PENTAGONAL_PYRAMID]: { + points: [ + [0, 0, figureSize, 1], + [0, 100, 0, 1], + [95.1, 30.9, 0, 1], + [58.8, -80.9, 0, 1], + [-58.8, -80.9, 0, 1], + [-95.1, 30.9, 0, 1], + ] as Point[], + faces: [ + [0, 1, 2], + [0, 2, 3], + [0, 3, 4], + [0, 4, 5], + [0, 5, 1], + [1, 2, 3, 4, 5], + ], + }, + }; + + const identityMatrix: Point[] = [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [0, 0, 0, 1], ]; - const faces = [ - [0, 1, 2], - [0, 2, 3], - [0, 3, 4], - [0, 4, 5], - [0, 5, 1], - [1, 2, 3, 4, 5], + const translationMatrix: Point[] = [ + [1, 0, 0, 0], + [0, -1, 0, 0], + [0, 0, 1, 0], + [centerX, centerY, 0, 1], ]; - // Cube - // const points: Point[] = [ - // [0, 0, 0, 1], - // [0, 0, 100, 1], - // [0, 100, 0, 1], - // [0, 100, 100, 1], - // [100, 0, 0, 1], - // [100, 0, 100, 1], - // [100, 100, 0, 1], - // [100, 100, 100, 1], - // ]; - // - // const faces = [ - // [0, 1, 3, 2], - // [0, 1, 5, 4], - // [0, 2, 6, 4], - // [1, 3, 7, 5], - // [2, 3, 7, 6], - // [4, 5, 7, 6], - // ]; - // Rotate around X axis const rotateX = (angle: number): Point[] => { const rad = (angle * Math.PI) / 180; @@ -129,13 +198,43 @@ onMounted(() => { ]; }; - const axonometryIsometric = (): Point[] => { - return [ + const projections = { + [Projections.NONE]: (): Point[] => identityMatrix, + + [Projections.ISOMETRIC]: (): Point[] => [ [0.707, -0.408, 0, 0], [0, 0.816, 0, 0], [-0.707, -0.408, 1, 0], [0, 0, 0, 1], - ]; + ], + + [Projections.DIMETRIC]: (): Point[] => [ + [0.926, 0.134, 0, 0], + [0, 0.935, 0, 0], + [0.378, -0.327, 0, 0], + [0, 0, 0, 1], + ], + + [Projections.TRIMETRIC]: (): Point[] => [ + [0.866, 0.354, 0, 0], + [0, 0.707, 0, 0], + [0.5, -0.612, 0, 0], + [0, 0, 0, 1], + ], + + [Projections.ONE_POINT_PERSPECTIVE]: (): Point[] => [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 0, 0.001], + [0, 0, 0, 1], + ], + + [Projections.TWO_POINT_PERSPECTIVE]: (): Point[] => [ + [1, 0, 0, 0.001], + [0, 1, 0, 0.001], + [0, 0, 0, 0], + [0, 0, 0, 1], + ], }; // Multiply array of points by matrix @@ -193,18 +292,27 @@ onMounted(() => { } }; - useTransformations((translation, rotation, scale) => { + const { currentFigure } = useFigure(); + + useTransformations((translation, rotation, scale, prj) => { + const matrix = mul([ + figureList[currentFigure.value].points, + scaleMatrix(scale[0], scale[1], scale[2]), + rotate(rotation[0], rotation[1], rotation[2]), + translate(translation[0], translation[1], translation[2]), + projections[prj](), + ]); + + for (let i = 0; i < matrix.length; i++) { + matrix[i][0] = matrix[i][0] / matrix[i][3]; + matrix[i][1] = matrix[i][1] / matrix[i][3]; + matrix[i][2] = matrix[i][2] / matrix[i][3]; + matrix[i][3] = 1; + } + drawFigure( - mul([ - points, - scaleMatrix(scale[0], scale[1], scale[2]), - rotate(rotation[0], rotation[1], rotation[2]), - translate(translation[0], translation[1], translation[2]), - rotateX(-90), - axonometryIsometric(), - translate(centerX, centerY + figureSizeH / 2, 0), - ]), - faces + mul([matrix, translationMatrix]), + figureList[currentFigure.value].faces ); }); }); diff --git a/src/components/form/range.vue b/src/components/form/range.vue index 9fa12dd..f68149e 100644 --- a/src/components/form/range.vue +++ b/src/components/form/range.vue @@ -5,12 +5,14 @@ const { label, min, max, + currentValue, defaultValue, step = 0.1, } = defineProps<{ label: string; min: number; max: number; + currentValue: number; defaultValue: number; step?: number; }>(); @@ -19,7 +21,7 @@ const emit = defineEmits<{ (event: 'change', value: number): void; }>(); -const value = ref(defaultValue); +const value = ref(currentValue); const onChange = (event: Event) => { const { target } = event as HTMLElementEvent; diff --git a/src/composables/useFigure.ts b/src/composables/useFigure.ts new file mode 100644 index 0000000..d4ae7c4 --- /dev/null +++ b/src/composables/useFigure.ts @@ -0,0 +1,20 @@ +export const enum Figures { + CUBE, + OCTAHEDRON, + TRIHEDRAL_PYRAMID, + SQUARE_PYRAMID, + PENTAGONAL_PYRAMID, +} + +const currentFigure = ref(Figures.PENTAGONAL_PYRAMID); + +export const useFigure = () => { + const setFigure = (figure: Figures) => { + currentFigure.value = figure; + }; + + return { + currentFigure, + setFigure, + }; +}; diff --git a/src/composables/states.ts b/src/composables/useTransformations.ts similarity index 55% rename from src/composables/states.ts rename to src/composables/useTransformations.ts index ac45323..d7dd184 100644 --- a/src/composables/states.ts +++ b/src/composables/useTransformations.ts @@ -1,10 +1,27 @@ +export const enum Projections { + NONE, + ISOMETRIC, + DIMETRIC, + TRIMETRIC, + ONE_POINT_PERSPECTIVE, + TWO_POINT_PERSPECTIVE, +} + export type XYZ = [number, number, number]; -type fn = ((translation: XYZ, rotation: XYZ, scale: XYZ) => void) | null; +type fn = + | (( + translation: XYZ, + rotation: XYZ, + scale: XYZ, + projection: Projections + ) => void) + | null; const translation = reactive([0, 0, 0]); const rotation = reactive([0, 0, 0]); const scale = reactive([1, 1, 1]); +const projection = ref(Projections.NONE); const setTranslation = (axis: number, value: number) => { translation[axis] = value; @@ -18,17 +35,23 @@ const setScale = (axis: number, value: number) => { scale[axis] = value; }; +const setProjection = (value: Projections) => { + projection.value = value; +}; + export const useTransformations = (onUpdate: fn = null) => { watchEffect(() => { - if (onUpdate) onUpdate(translation, rotation, scale); + if (onUpdate) onUpdate(translation, rotation, scale, projection.value); }); return { translation, rotation, scale, + projection, setTranslation, setRotation, setScale, + setProjection, }; }; diff --git a/src/layouts/default.vue b/src/layouts/default.vue index 7fd3a30..9237b1a 100644 --- a/src/layouts/default.vue +++ b/src/layouts/default.vue @@ -1,5 +1,5 @@