├── .editorconfig
├── .eslintrc.json
├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── Bug_report.md
│ └── Feature_request.md
└── workflows
│ ├── CI.yml
│ └── publish-on-npm.yml
├── .gitignore
├── .storybook
├── main.ts
├── preview.ts
├── tsconfig.json
└── typings.d.ts
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── angular.json
├── karma.conf.js
├── package-lock.json
├── package.json
├── projects
└── atft
│ ├── .storybook
│ ├── main.ts
│ ├── preview.ts
│ ├── tsconfig.json
│ └── typings.d.ts
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── karma.conf.js
│ ├── ng-package.json
│ ├── package.json
│ ├── src
│ ├── index.spec.ts
│ ├── lib
│ │ ├── actor
│ │ │ ├── data-center
│ │ │ │ ├── atft-data-center-actor.module.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── layer
│ │ │ │ │ ├── grid-actor.component.spec.ts
│ │ │ │ │ ├── grid-actor.component.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── layer-actor.component.spec.ts
│ │ │ │ │ └── layer-actor.component.ts
│ │ │ │ ├── layout
│ │ │ │ │ ├── dagre-composition.component.spec.ts
│ │ │ │ │ ├── dagre-composition.component.ts
│ │ │ │ │ ├── dagre-edge.component.spec.ts
│ │ │ │ │ ├── dagre-edge.component.ts
│ │ │ │ │ ├── dagre-layout.component.spec.ts
│ │ │ │ │ ├── dagre-layout.component.ts
│ │ │ │ │ ├── dagre-model.ts
│ │ │ │ │ ├── dagre-node.component.ts
│ │ │ │ │ ├── dagre-utils.spec.ts
│ │ │ │ │ ├── dagre-utils.ts
│ │ │ │ │ ├── dagre-yaml-parser.component.spec.ts
│ │ │ │ │ ├── dagre-yaml-parser.component.ts
│ │ │ │ │ └── index.ts
│ │ │ │ ├── server
│ │ │ │ │ ├── abstract-server-actor.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── model-actor.component.ts
│ │ │ │ │ ├── server-barrel-actor.component.ts
│ │ │ │ │ ├── server-compact-actor.component.ts
│ │ │ │ │ ├── server-icon-actor.component.ts
│ │ │ │ │ ├── server-stand-actor.component.spec.ts
│ │ │ │ │ ├── server-stand-actor.component.ts
│ │ │ │ │ └── workstation-actor.component.ts
│ │ │ │ └── service
│ │ │ │ │ ├── actor-repository.service.ts
│ │ │ │ │ └── index.ts
│ │ │ └── ux
│ │ │ │ ├── index.ts
│ │ │ │ ├── loader
│ │ │ │ ├── index.ts
│ │ │ │ ├── loader-actor.component.spec.ts
│ │ │ │ └── loader-actor.component.ts
│ │ │ │ ├── text
│ │ │ │ ├── index.ts
│ │ │ │ ├── text-actor.component.spec.ts
│ │ │ │ └── text-actor.component.ts
│ │ │ │ └── ux-actor.module.ts
│ │ ├── animation
│ │ │ ├── animation.service.spec.ts
│ │ │ ├── animation.service.ts
│ │ │ ├── atft-animation.module.ts
│ │ │ ├── index.ts
│ │ │ └── timeline-emitter
│ │ │ │ ├── emit-step.component.ts
│ │ │ │ ├── index.ts
│ │ │ │ └── timeline-emitter.component.ts
│ │ ├── atft.module.ts
│ │ ├── camera
│ │ │ ├── abstract-camera.ts
│ │ │ ├── atft-camera.module.ts
│ │ │ ├── index.ts
│ │ │ ├── orthographic-camera.component.ts
│ │ │ └── perspective-camera.component.ts
│ │ ├── control
│ │ │ ├── abstract-orbit-controls.ts
│ │ │ ├── atft-control.module.ts
│ │ │ ├── controls.component.scss
│ │ │ ├── index.ts
│ │ │ ├── map-controls.component.spec.ts
│ │ │ ├── map-controls.component.ts
│ │ │ ├── orbit-controls.component.spec.ts
│ │ │ └── orbit-controls.component.ts
│ │ ├── effect
│ │ │ ├── atft-effect.module.ts
│ │ │ ├── compose
│ │ │ │ ├── abstract-compose-effect.ts
│ │ │ │ ├── blur.component.ts
│ │ │ │ ├── dof.component.ts
│ │ │ │ ├── dot-screen.component.ts
│ │ │ │ ├── effect-composer.component.spec.ts
│ │ │ │ └── effect-composer.component.ts
│ │ │ ├── dashed-draw.directive.spec.ts
│ │ │ ├── dashed-draw.directive.ts
│ │ │ ├── fog.component.ts
│ │ │ └── index.ts
│ │ ├── object
│ │ │ ├── abstract-empty-directive.ts
│ │ │ ├── abstract-lazy-object-3d.ts
│ │ │ ├── abstract-object-3d.ts
│ │ │ ├── atft-object.module.ts
│ │ │ ├── connector
│ │ │ │ ├── abstract-connector.ts
│ │ │ │ ├── atft-connector.module.ts
│ │ │ │ ├── index.ts
│ │ │ │ └── line-connector.component.ts
│ │ │ ├── content-projection.component.ts
│ │ │ ├── helper
│ │ │ │ ├── atft-helper.module.ts
│ │ │ │ ├── axes-helper.component.spec.ts
│ │ │ │ ├── axes-helper.component.ts
│ │ │ │ ├── empty.component.ts
│ │ │ │ ├── grid-helper.component.spec.ts
│ │ │ │ ├── grid-helper.component.ts
│ │ │ │ └── index.ts
│ │ │ ├── index.ts
│ │ │ ├── light
│ │ │ │ ├── ambient-light.component.ts
│ │ │ │ ├── atft-light.module.ts
│ │ │ │ ├── directional-light.component.spec.ts
│ │ │ │ ├── directional-light.component.ts
│ │ │ │ ├── hemisphere-light.component.spec.ts
│ │ │ │ ├── hemisphere-light.component.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── point-light.component.spec.ts
│ │ │ │ └── point-light.component.ts
│ │ │ ├── loader
│ │ │ │ ├── abstract-model-loader.ts
│ │ │ │ ├── atft-loader.module.ts
│ │ │ │ ├── audio-loader.component.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── obj-loader.component.spec.ts
│ │ │ │ ├── obj-loader.component.ts
│ │ │ │ ├── object-loader.component.spec.ts
│ │ │ │ ├── object-loader.component.ts
│ │ │ │ ├── services
│ │ │ │ │ ├── abstract-asset.service.ts
│ │ │ │ │ ├── abstract-cache.service.ts
│ │ │ │ │ ├── font.service.ts
│ │ │ │ │ ├── icon.service.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── model.service.ts
│ │ │ │ │ ├── obj-loader.service.ts
│ │ │ │ │ └── svg-loader.service.ts
│ │ │ │ ├── stl-loader.component.spec.ts
│ │ │ │ ├── stl-loader.component.ts
│ │ │ │ ├── svg-loader.component.spec.ts
│ │ │ │ └── svg-loader.component.ts
│ │ │ ├── mesh
│ │ │ │ ├── abstract-mesh-3d.ts
│ │ │ │ ├── abstract-mesh.spec.ts
│ │ │ │ ├── atft-mesh.module.ts
│ │ │ │ ├── box-mesh.component.spec.ts
│ │ │ │ ├── box-mesh.component.ts
│ │ │ │ ├── cylinder-mesh.component.spec.ts
│ │ │ │ ├── cylinder-mesh.component.ts
│ │ │ │ ├── frame-mesh.component.spec.ts
│ │ │ │ ├── frame-mesh.component.ts
│ │ │ │ ├── grid-mesh.component.spec.ts
│ │ │ │ ├── grid-mesh.component.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── plane-mesh.component.spec.ts
│ │ │ │ ├── plane-mesh.component.ts
│ │ │ │ ├── sphere-mesh.component.spec.ts
│ │ │ │ ├── sphere-mesh.component.ts
│ │ │ │ ├── torus-mesh.component.spec.ts
│ │ │ │ ├── torus-mesh.component.ts
│ │ │ │ └── video-mesh.component.ts
│ │ │ ├── scene.component.ts
│ │ │ └── text
│ │ │ │ ├── atft-text.module.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── text-mesh.component.spec.ts
│ │ │ │ └── text-mesh.component.ts
│ │ ├── pipe
│ │ │ ├── atft-pipe.module.ts
│ │ │ ├── deg2rad.pipe.ts
│ │ │ ├── index.ts
│ │ │ └── rad2deg.pipe.ts
│ │ ├── raycaster
│ │ │ ├── atft-raycaster.module.ts
│ │ │ ├── index.ts
│ │ │ ├── raycaster-camera.directive.spec.ts
│ │ │ ├── raycaster-camera.directive.ts
│ │ │ ├── raycaster-enable.directive.spec.ts
│ │ │ ├── raycaster-enable.directive.ts
│ │ │ ├── raycaster-event.ts
│ │ │ ├── raycaster-group.directive.spec.ts
│ │ │ ├── raycaster-group.directive.ts
│ │ │ ├── raycaster.service.spec.ts
│ │ │ └── raycaster.service.ts
│ │ ├── renderer
│ │ │ ├── atft-renderer.module.ts
│ │ │ ├── bloom.service.ts
│ │ │ ├── index.ts
│ │ │ ├── renderer-canvas.component.html
│ │ │ ├── renderer-canvas.component.scss
│ │ │ ├── renderer-canvas.component.ts
│ │ │ └── renderer.service.ts
│ │ ├── stats
│ │ │ ├── index.ts
│ │ │ ├── stats-auto-show.directive.ts
│ │ │ ├── stats.module.ts
│ │ │ ├── stats.service.spec.ts
│ │ │ └── stats.service.ts
│ │ ├── threejs-fork
│ │ │ ├── SVGLoader.ts
│ │ │ └── index.ts
│ │ └── util
│ │ │ ├── applied-material.spec.ts
│ │ │ ├── applied-material.ts
│ │ │ ├── calculate-size.spec.ts
│ │ │ ├── calculate-size.ts
│ │ │ ├── fix-center.spec.ts
│ │ │ ├── fix-center.ts
│ │ │ ├── index.ts
│ │ │ ├── provide-parent.ts
│ │ │ ├── removeFromArray.ts
│ │ │ ├── scale-to-fit.spec.ts
│ │ │ └── scale-to-fit.ts
│ ├── public_api.ts
│ └── test.ts
│ ├── tsconfig.lib.json
│ ├── tsconfig.lib.prod.json
│ └── tsconfig.spec.json
├── src
├── app
│ ├── app-routing.module.ts
│ ├── app.component.html
│ ├── app.component.scss
│ ├── app.component.spec.ts
│ ├── app.component.ts
│ └── app.module.ts
├── assets
│ ├── .gitkeep
│ ├── audio
│ │ ├── sample1.mp3
│ │ └── sample2.mp3
│ ├── font
│ │ └── helvetiker_regular.typeface.json
│ ├── model
│ │ ├── Menger_sponge_sample.stl
│ │ ├── Server.json
│ │ └── smiley
│ │ │ ├── Smiley_Sphere001_5.jpg
│ │ │ ├── smiley.mtl
│ │ │ └── smiley.obj
│ └── svg
│ │ ├── App-Services.svg
│ │ ├── border-none-solid.svg
│ │ ├── expeditedssl-brands.svg
│ │ ├── external-link-alt-solid.svg
│ │ ├── galactic-republic-brands.svg
│ │ ├── grid-world.svg
│ │ ├── idea.svg
│ │ ├── server-solid.svg
│ │ ├── sitemap-solid.svg
│ │ ├── upload.svg
│ │ ├── users-solid.svg
│ │ └── worldwide.svg
├── environments
│ ├── environment.prod.ts
│ └── environment.ts
├── favicon.ico
├── index.html
├── main.ts
├── stories
│ ├── all-in-one
│ │ ├── infrastructure.stories.ts
│ │ └── virtual-city.stories.ts
│ ├── animation
│ │ ├── animate-loop.stories.ts
│ │ ├── animate-mixer.stories.ts
│ │ ├── animate-reactive-grid.stories.ts
│ │ └── connector
│ │ │ ├── connector-line-animated.stories.ts
│ │ │ ├── connector-line-bloom.stories.ts
│ │ │ └── connector-line.stories.ts
│ ├── assets
│ │ ├── accessibility.png
│ │ ├── accessibility.svg
│ │ ├── addon-library.png
│ │ ├── assets.png
│ │ ├── context.png
│ │ ├── discord.svg
│ │ ├── docs.png
│ │ ├── figma-plugin.png
│ │ ├── github.svg
│ │ ├── share.png
│ │ ├── styling.png
│ │ ├── testing.png
│ │ ├── theming.png
│ │ ├── tutorials.svg
│ │ └── youtube.svg
│ ├── basic
│ │ ├── dynamic
│ │ │ ├── editor.stories.ts
│ │ │ ├── for-loop.stories.ts
│ │ │ ├── ng-content.stories.ts
│ │ │ └── routing.stories.ts
│ │ ├── loader
│ │ │ ├── loader-audio.stories.ts
│ │ │ ├── loader-obj.stories.ts
│ │ │ ├── loader-object.stories.ts
│ │ │ ├── loader-stl.stories.ts
│ │ │ └── loader-svg.stories.ts
│ │ ├── mesh
│ │ │ ├── mesh-box.stories.ts
│ │ │ ├── mesh-server.stories.ts
│ │ │ ├── mesh-text.stories.ts
│ │ │ └── mesh-video.stories.ts
│ │ ├── object
│ │ │ ├── object-modification.stories.ts
│ │ │ └── object-raycaster.stories.ts
│ │ └── timeline
│ │ │ └── timeline-emitter.stories.ts
│ ├── dagre-layout
│ │ ├── dagre-composition.stories.ts
│ │ ├── dagre-dynamic.stories.ts
│ │ ├── dagre-icons.stories.ts
│ │ ├── dagre-loop.stories.ts
│ │ ├── dagre-route.stories.ts
│ │ └── dagre-simple-layout.stories.ts
│ ├── effect
│ │ ├── blur.stories.ts
│ │ ├── dashed-draw.stories.ts
│ │ ├── depth-of-field.stories.ts
│ │ ├── dot-screen.stories.ts
│ │ └── fog.stories.ts
│ ├── scene-wrapper
│ │ ├── axes-scene-wrapper.ts
│ │ ├── effects-scene-wrapper.ts
│ │ ├── performance-scene-wrapper.ts
│ │ ├── ux-scene-wrapper.ts
│ │ └── world-scene-wrapper.ts
│ └── ux
│ │ ├── intro.stories.ts
│ │ ├── loader.stories.ts
│ │ └── text.stories.ts
└── styles.scss
├── tsconfig.app.json
├── tsconfig.json
└── tsconfig.spec.json
/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see https://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.ts]
12 | quote_type = single
13 |
14 | [*.md]
15 | max_line_length = off
16 | trim_trailing_whitespace = false
17 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "overrides": [
4 | {
5 | "files": [
6 | "*.ts"
7 | ],
8 | "parserOptions": {
9 | "project": [
10 | "tsconfig.json",
11 | "e2e/tsconfig.json"
12 | ],
13 | "createDefaultProgram": true
14 | },
15 | "extends": [
16 | "plugin:@angular-eslint/recommended",
17 | "eslint:recommended",
18 | "plugin:@typescript-eslint/recommended",
19 | "plugin:@typescript-eslint/recommended-requiring-type-checking",
20 | "plugin:@angular-eslint/template/process-inline-templates"
21 | ],
22 | "rules": {
23 | "@angular-eslint/component-selector": 0,
24 | "@angular-eslint/directive-selector": 0,
25 | "@typescript-eslint/no-unsafe-call": 0,
26 | "@typescript-eslint/no-unsafe-member-access": 0,
27 | "@typescript-eslint/no-unsafe-assignment": 0,
28 | "@typescript-eslint/no-unsafe-return": 0,
29 | "@typescript-eslint/no-unsafe-argument": 0,
30 | "@typescript-eslint/no-explicit-any": 0,
31 | "@typescript-eslint/explicit-module-boundary-types": 0,
32 | "@typescript-eslint/unbound-method": 0,
33 | "@angular-eslint/no-output-native": 0,
34 | "@typescript-eslint/no-unused-vars": 2,
35 | "@typescript-eslint/ban-ts-comment": 0,
36 | "storybook/context-in-play-function": 0
37 | }
38 | },
39 | {
40 | "files": [
41 | "*.spec.ts"
42 | ],
43 | "rules": {
44 | "@typescript-eslint/no-floating-promises": 0
45 | }
46 | },
47 | {
48 | "files": [
49 | "*.html"
50 | ],
51 | "extends": [
52 | "plugin:@angular-eslint/template/recommended"
53 | ],
54 | "rules": {}
55 | }
56 | ],
57 | "extends": [
58 | "plugin:storybook/recommended"
59 | ]
60 | }
61 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: makimenko
4 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/Bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 |
5 | ---
6 |
7 | ## Steps
8 | 1.
9 |
10 | ## Actual Result
11 |
12 | ## Expected Result
13 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/Feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 |
5 | ---
6 |
7 | **Is your feature request related to a problem? Please describe.**
8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
9 |
10 | **Describe the solution you'd like**
11 | A clear and concise description of what you want to happen.
12 |
13 | **Describe alternatives you've considered**
14 | A clear and concise description of any alternative solutions or features you've considered.
15 |
16 | **Additional context**
17 | Add any other context or screenshots about the feature request here.
18 |
--------------------------------------------------------------------------------
/.github/workflows/publish-on-npm.yml:
--------------------------------------------------------------------------------
1 | name: Publish on NPM
2 |
3 | on:
4 | release:
5 | types: [published]
6 |
7 | jobs:
8 | build:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - uses: actions/checkout@v1
12 | - uses: actions/setup-node@v1
13 | with:
14 | node-version: 12
15 | registry-url: https://registry.npmjs.org/
16 | - name: Install project dependencies
17 | run: npm i
18 | - name: Build atft library
19 | run: npx ng build atft
20 | - run: npm publish --access public
21 | working-directory: ./dist/atft
22 | env:
23 | NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
24 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # Compiled output
4 | /dist
5 | /tmp
6 | /out-tsc
7 | /bazel-out
8 |
9 | # Node
10 | /node_modules
11 | npm-debug.log
12 | yarn-error.log
13 |
14 | # IDEs and editors
15 | .idea/
16 | .project
17 | .classpath
18 | .c9/
19 | *.launch
20 | .settings/
21 | *.sublime-workspace
22 |
23 | # Visual Studio Code
24 | .vscode/*
25 | !.vscode/settings.json
26 | !.vscode/tasks.json
27 | !.vscode/launch.json
28 | !.vscode/extensions.json
29 | .history/*
30 |
31 | # Miscellaneous
32 | /.angular/cache
33 | .sass-cache/
34 | /connect.lock
35 | /coverage
36 | /libpeerconnection.log
37 | testem.log
38 | /typings
39 |
40 | # System files
41 | .DS_Store
42 | Thumbs.db
43 | /.angular/
44 |
45 | *.sh
46 |
--------------------------------------------------------------------------------
/.storybook/main.ts:
--------------------------------------------------------------------------------
1 | import type {StorybookConfig} from "@storybook/angular";
2 |
3 | const config: StorybookConfig = {
4 | stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"],
5 | addons: [
6 | "@storybook/addon-links",
7 | "@storybook/addon-essentials",
8 | "@storybook/addon-interactions",
9 | ],
10 | framework: {
11 | name: "@storybook/angular",
12 | options: {},
13 | },
14 | docs: {
15 | autodocs: "tag",
16 | },
17 | staticDirs: [
18 | {from: '../src/assets', to: '/assets' }
19 | ], previewHead: (head) => `
20 |
36 | ${head}
37 | `,
38 | };
39 | export default config;
40 |
--------------------------------------------------------------------------------
/.storybook/preview.ts:
--------------------------------------------------------------------------------
1 | import type { Preview } from "@storybook/angular";
2 |
3 | const preview: Preview = {
4 | parameters: {
5 | layout: 'fullscreen',
6 | actions: { argTypesRegex: "^on[A-Z].*" },
7 | controls: {
8 | matchers: {
9 | color: /(background|color)$/i,
10 | date: /Date$/i,
11 | },
12 | },
13 | options: {
14 | storySort: {method: 'alphabetical'}
15 | },
16 | },
17 | };
18 |
19 | export default preview;
20 |
--------------------------------------------------------------------------------
/.storybook/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.app.json",
3 | "compilerOptions": {
4 | "types": ["node"],
5 | "allowSyntheticDefaultImports": true,
6 | "resolveJsonModule": true
7 | },
8 | "exclude": ["../src/test.ts", "../src/**/*.spec.ts"],
9 | "include": ["../src/**/*", "./preview.ts"],
10 | "files": ["./typings.d.ts"]
11 | }
12 |
--------------------------------------------------------------------------------
/.storybook/typings.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.md' {
2 | const content: string;
3 | export default content;
4 | }
5 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4 |
5 | ### [0.0.1](https://github.com/makimenko/angular-template-for-threejs/compare/v1.5.4...v0.0.1) (2023-12-04)
6 |
7 |
8 | ### Features
9 |
10 | * Upgrade to Angular 17 ([c28180a](https://github.com/makimenko/angular-template-for-threejs/commit/c28180a7230ebf974295cf3cbf8c8c61a64aab15))
11 |
12 |
13 | ### Bug Fixes
14 |
15 | * light intensity ([d8a9455](https://github.com/makimenko/angular-template-for-threejs/commit/d8a94558b0429c76220e2fb415b46d4c7075f52e))
16 | * upgrade three from 0.158.0 to 0.159.0 ([#442](https://github.com/makimenko/angular-template-for-threejs/issues/442)) ([71c3760](https://github.com/makimenko/angular-template-for-threejs/commit/71c37609e406c2bc89b812dd7fd4eb0d14da206d))
17 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Mihail Akimenko
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/1.0/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: 'src',
7 | frameworks: ['jasmine', '@angular-devkit/build-angular'],
8 | plugins: [
9 | require('karma-jasmine'),
10 | require('karma-chrome-launcher'),
11 | require('karma-jasmine-html-reporter'),
12 | require('karma-coverage'),
13 | require('@angular-devkit/build-angular/plugins/karma')
14 | ],
15 | client: {
16 | jasmine: {
17 | // you can add configuration options for Jasmine here
18 | // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
19 | // for example, you can disable the random execution with `random: false`
20 | // or set a specific seed with `seed: 4321`
21 | },
22 | clearContext: false // leave Jasmine Spec Runner output visible in browser
23 | },
24 | customLaunchers: {
25 | ChromeHeadlessCustom: {
26 | base: 'ChromeHeadless',
27 | flags: ['--no-sandbox', '--disable-gpu']
28 | }
29 | },
30 | jasmineHtmlReporter: {
31 | suppressAll: true // removes the duplicated traces
32 | },
33 | coverageReporter: {
34 | dir: require('path').join(__dirname, './coverage/app'),
35 | subdir: '.',
36 | reporters: [
37 | { type: 'html' },
38 | { type: 'text-summary' },
39 | { type: 'lcov', subdir: 'report-lcov' },
40 | ]
41 | },
42 | preprocessors: {
43 | 'src/**/*.ts': 'coverage'
44 | },
45 | reporters: ['progress', 'kjhtml', 'coverage'],
46 | port: 9876,
47 | colors: true,
48 | logLevel: config.LOG_INFO,
49 | autoWatch: true,
50 | browsers: ['Chrome'],
51 | singleRun: false,
52 | restartOnFileChange: true
53 | });
54 | };
55 |
--------------------------------------------------------------------------------
/projects/atft/.storybook/main.ts:
--------------------------------------------------------------------------------
1 | import type { StorybookConfig } from "@storybook/angular";
2 |
3 | const config: StorybookConfig = {
4 | stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"],
5 | addons: [
6 | "@storybook/addon-links",
7 | "@storybook/addon-essentials",
8 | "@storybook/addon-interactions",
9 | ],
10 | framework: {
11 | name: "@storybook/angular",
12 | options: {},
13 | },
14 | docs: {
15 | autodocs: "tag",
16 | },
17 | };
18 | export default config;
19 |
--------------------------------------------------------------------------------
/projects/atft/.storybook/preview.ts:
--------------------------------------------------------------------------------
1 | import type { Preview } from "@storybook/angular";
2 |
3 | const preview: Preview = {
4 | parameters: {
5 | actions: { argTypesRegex: "^on[A-Z].*" },
6 | controls: {
7 | matchers: {
8 | color: /(background|color)$/i,
9 | date: /Date$/i,
10 | },
11 | },
12 | },
13 | };
14 |
15 | export default preview;
16 |
--------------------------------------------------------------------------------
/projects/atft/.storybook/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.lib.json",
3 | "compilerOptions": {
4 | "types": ["node"],
5 | "allowSyntheticDefaultImports": true,
6 | "resolveJsonModule": true
7 | },
8 | "exclude": ["../src/test.ts", "../src/**/*.spec.ts"],
9 | "include": ["../src/**/*", "./preview.ts"],
10 | "files": ["./typings.d.ts"]
11 | }
12 |
--------------------------------------------------------------------------------
/projects/atft/.storybook/typings.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.md' {
2 | const content: string;
3 | export default content;
4 | }
5 |
--------------------------------------------------------------------------------
/projects/atft/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/1.0/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: '',
7 | frameworks: ['jasmine', '@angular-devkit/build-angular'],
8 | plugins: [
9 | require('karma-jasmine'),
10 | require('karma-chrome-launcher'),
11 | require('karma-jasmine-html-reporter'),
12 | require('karma-coverage'),
13 | require('@angular-devkit/build-angular/plugins/karma')
14 | ],
15 | client: {
16 | jasmine: {
17 | // you can add configuration options for Jasmine here
18 | // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
19 | // for example, you can disable the random execution with `random: false`
20 | // or set a specific seed with `seed: 4321`
21 | },
22 | clearContext: false // leave Jasmine Spec Runner output visible in browser
23 | },
24 | customLaunchers: {
25 | ChromeHeadlessCustom: {
26 | base: 'ChromeHeadless',
27 | flags: ['--no-sandbox', '--disable-gpu']
28 | }
29 | },
30 | jasmineHtmlReporter: {
31 | suppressAll: true // removes the duplicated traces
32 | },
33 | coverageReporter: {
34 | dir: require('path').join(__dirname, '../../coverage'),
35 | subdir: '.',
36 | reporters: [
37 | { type: 'html' },
38 | { type: 'text-summary' },
39 | { type: 'lcov', subdir: 'report-lcov' },
40 | ]
41 | },
42 | preprocessors: {
43 | 'src/**/*.ts': 'coverage'
44 | },
45 | reporters: ['progress', 'kjhtml', 'coverage'],
46 | port: 9876,
47 | colors: true,
48 | logLevel: config.LOG_INFO,
49 | autoWatch: true,
50 | browsers: ['Chrome'],
51 | singleRun: false,
52 | restartOnFileChange: true
53 | });
54 | };
55 |
--------------------------------------------------------------------------------
/projects/atft/ng-package.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
3 | "dest": "../../dist/atft",
4 | "lib": {
5 | "entryFile": "src/public_api.ts"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/projects/atft/src/index.spec.ts:
--------------------------------------------------------------------------------
1 | const orgConsoleError = window.console.error;
2 |
3 | export function patchConsoleToFailOnError() {
4 | window.console.error = function (...args: any[]) {
5 | orgConsoleError.apply(this, args);
6 | if (args && args.toString() !== '[object ProgressEvent]') {
7 | try {
8 | throw new Error('console.error');
9 | } catch (err) {
10 | /* eslint-disable no-console */
11 | console.info('console.error', args, err);
12 | }
13 | fail('console.error was called, this is not allowed in a unit test run');
14 | }
15 | };
16 | }
17 |
18 |
19 | beforeAll(() => patchConsoleToFailOnError());
20 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/actor/data-center/index.ts:
--------------------------------------------------------------------------------
1 | export * from './atft-data-center-actor.module';
2 | export * from './layer';
3 | export * from './server';
4 | export * from './layout';
5 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/actor/data-center/layer/grid-actor.component.spec.ts:
--------------------------------------------------------------------------------
1 | import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';
2 | import {AtftDataCenterActorModule} from '../atft-data-center-actor.module';
3 | import {RendererService} from '../../../renderer';
4 | import {AnimationService} from '../../../animation';
5 | import {StatsService} from '../../../stats';
6 | import {GridActorComponent} from './grid-actor.component';
7 |
8 | describe('actor', () => {
9 | describe('GridActorComponent', () => {
10 | let component: GridActorComponent;
11 | let fixture: ComponentFixture;
12 |
13 | beforeEach(waitForAsync(() => {
14 | TestBed.configureTestingModule({
15 | imports: [
16 | AtftDataCenterActorModule
17 | ],
18 | providers: [
19 | StatsService,
20 | RendererService,
21 | AnimationService
22 | ]
23 | });
24 | fixture = TestBed.createComponent(GridActorComponent);
25 | component = fixture.componentInstance;
26 | return TestBed.compileComponents();
27 | }));
28 |
29 |
30 | it('init', waitForAsync(() => {
31 | fixture.detectChanges();
32 | expect(component).toBeTruthy();
33 | }));
34 |
35 | });
36 | });
37 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/actor/data-center/layer/index.ts:
--------------------------------------------------------------------------------
1 | export * from './layer-actor.component';
2 | export * from './grid-actor.component';
3 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/actor/data-center/layer/layer-actor.component.spec.ts:
--------------------------------------------------------------------------------
1 | import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';
2 | import {AtftDataCenterActorModule} from '../atft-data-center-actor.module';
3 | import {RendererService} from '../../../renderer';
4 | import {AnimationService} from '../../../animation';
5 | import {LayerActorComponent} from './layer-actor.component';
6 | import {StatsService} from '../../../stats';
7 |
8 | describe('actor', () => {
9 | describe('LayerActorComponent', () => {
10 | let component: LayerActorComponent;
11 | let fixture: ComponentFixture;
12 |
13 | beforeEach(waitForAsync(() => {
14 | TestBed.configureTestingModule({
15 | imports: [
16 | AtftDataCenterActorModule
17 | ],
18 | providers: [
19 | StatsService,
20 | RendererService,
21 | AnimationService
22 | ]
23 | });
24 | fixture = TestBed.createComponent(LayerActorComponent);
25 | component = fixture.componentInstance;
26 | return TestBed.compileComponents();
27 | }));
28 |
29 |
30 | it('init', waitForAsync(() => {
31 | fixture.detectChanges();
32 | expect(component).toBeTruthy();
33 |
34 | component.onSelected();
35 | component.onDeselected();
36 | component.onClick();
37 |
38 | }));
39 |
40 |
41 | });
42 | });
43 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/actor/data-center/layer/layer-actor.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, EventEmitter, Input, Output } from '@angular/core';
2 | import { AbstractEmptyDirective } from '../../../object';
3 | import { provideParent } from '../../../util';
4 |
5 |
6 | @Component({
7 | selector: 'atft-layer-actor',
8 | providers: [provideParent(LayerActorComponent)],
9 | template: `
10 |
12 |
14 |
15 |
16 | `
17 | })
18 | export class LayerActorComponent extends AbstractEmptyDirective {
19 | @Input()
20 | label!: string;
21 |
22 | @Input()
23 | set width(width: number) {
24 | this._width = width;
25 | this.translateLabelX = this._width / 2 - 10;
26 | }
27 |
28 | get width(): number {
29 | return this._width;
30 | }
31 |
32 | protected _width!: number;
33 |
34 | @Input()
35 | height!: number;
36 |
37 | @Output() render = new EventEmitter();
38 | @Output() selected = new EventEmitter();
39 | @Output() deselected = new EventEmitter();
40 |
41 | color: string | number = '#A0A0A0';
42 |
43 | translateLabelX!: number;
44 |
45 | public onSelected() {
46 | this.color = '#A4A4A4';
47 | }
48 |
49 | public onDeselected() {
50 | this.color = '#A0A0A0';
51 | }
52 |
53 | public onClick() {
54 | this.color = '#A0A0A0';
55 | }
56 |
57 | }
58 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/actor/data-center/layout/dagre-edge.component.spec.ts:
--------------------------------------------------------------------------------
1 | import {ComponentFixture, TestBed} from '@angular/core/testing';
2 | import {StatsService} from '../../../stats';
3 | import {RendererService} from '../../../renderer';
4 | import {AnimationService} from '../../../animation';
5 | import {FontService, IconService, ModelService, ObjLoaderService, SvgLoaderService} from '../../../object';
6 |
7 | import {ActorRepositoryService} from '../service';
8 | import {RaycasterService} from '../../../raycaster';
9 | import {DagreEdgeComponent, EdgeType} from './dagre-edge.component';
10 | import {DagreLayoutComponent} from './dagre-layout.component';
11 |
12 | describe('dagre-layout', () => {
13 |
14 | describe('DagreEdgeComponent', () => {
15 | let component: DagreEdgeComponent;
16 | let fixture: ComponentFixture;
17 |
18 | beforeEach(() => {
19 | TestBed.configureTestingModule({
20 | declarations: [
21 | DagreEdgeComponent
22 | ],
23 | providers: [
24 | StatsService,
25 | RendererService,
26 | AnimationService,
27 | FontService,
28 | ActorRepositoryService,
29 | ModelService,
30 | DagreLayoutComponent,
31 | RaycasterService,
32 | ObjLoaderService,
33 | SvgLoaderService,
34 | IconService
35 | ]
36 | });
37 | fixture = TestBed.createComponent(DagreEdgeComponent);
38 | TestBed.compileComponents();
39 | });
40 |
41 | it('init', () => {
42 | component = fixture.componentInstance;
43 | component.type = EdgeType.association;
44 | fixture.detectChanges();
45 | expect(component).toBeTruthy();
46 |
47 | component.ngOnChanges({});
48 | component.ngOnDestroy();
49 |
50 | });
51 |
52 | });
53 |
54 | });
55 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/actor/data-center/layout/dagre-layout.component.spec.ts:
--------------------------------------------------------------------------------
1 | import {ComponentFixture, TestBed} from '@angular/core/testing';
2 | import {StatsService} from '../../../stats';
3 | import {RendererService} from '../../../renderer';
4 | import {AnimationService} from '../../../animation';
5 | import {FontService, IconService, ModelService, ObjLoaderService, SvgLoaderService} from '../../../object';
6 |
7 | import {ActorRepositoryService} from '../service';
8 | import {DagreLayoutComponent} from './dagre-layout.component';
9 | import {RaycasterService} from '../../../raycaster';
10 |
11 | describe('dagre-layout', () => {
12 |
13 | describe('DagreLayoutComponent', () => {
14 | let component: DagreLayoutComponent;
15 | let fixture: ComponentFixture;
16 |
17 | beforeEach(() => {
18 | TestBed.configureTestingModule({
19 | declarations: [
20 | DagreLayoutComponent
21 | ],
22 | providers: [
23 | StatsService,
24 | RendererService,
25 | AnimationService,
26 | FontService,
27 | ActorRepositoryService,
28 | ModelService,
29 | RaycasterService,
30 | DagreLayoutComponent,
31 | ObjLoaderService,
32 | SvgLoaderService,
33 | IconService
34 | ]
35 |
36 | });
37 | fixture = TestBed.createComponent(DagreLayoutComponent);
38 | TestBed.compileComponents();
39 | });
40 |
41 | it('init', () => {
42 | component = fixture.componentInstance;
43 | fixture.detectChanges();
44 | expect(component).toBeTruthy();
45 |
46 | component.ngOnChanges({});
47 | component.ngOnDestroy();
48 |
49 | });
50 |
51 | });
52 |
53 | });
54 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/actor/data-center/layout/dagre-model.ts:
--------------------------------------------------------------------------------
1 | import * as dagre from 'dagre';
2 |
3 | export interface BaseInfo {
4 | name: string;
5 | label?: string;
6 | composition?: string;
7 | }
8 |
9 | export interface Node extends BaseInfo {
10 | type?: string;
11 | icon?: string;
12 | model?: string;
13 | }
14 |
15 | export interface Composition extends BaseInfo {
16 | border?: string;
17 | }
18 |
19 | export interface Edge extends BaseInfo {
20 | from: string;
21 | to: string;
22 | type?: string;
23 | color?: number;
24 | }
25 |
26 | export interface GraphModel {
27 | layout?: dagre.GraphLabel;
28 | compositions?: Array;
29 | nodes?: Array;
30 | edges?: Array;
31 | }
32 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/actor/data-center/layout/dagre-utils.spec.ts:
--------------------------------------------------------------------------------
1 | import {DagreUtils} from './dagre-utils';
2 | import {GraphModel} from './dagre-model';
3 | import * as dagre from 'dagre';
4 |
5 | describe('dagre-layout', () => {
6 | describe('dagre-utils', () => {
7 |
8 | it('single node', () => {
9 | const model: GraphModel = {
10 | layout: {
11 | width: 100,
12 | height: 100
13 | },
14 | nodes: [{
15 | name: 'user',
16 | model: 'user',
17 | }]
18 | };
19 | const graph: dagre.graphlib.Graph = DagreUtils.modelToGraph(model);
20 | expect(graph.nodes().length).toBe(1);
21 | expect(graph.nodes()[0]).toBe('user');
22 | });
23 |
24 | it('basic graph', () => {
25 | const model: GraphModel = {
26 | layout: {
27 | width: 100,
28 | height: 100
29 | },
30 | nodes: [{
31 | name: 'user',
32 | model: 'user',
33 | }, {
34 | name: 'spa',
35 | type: 'compact',
36 | icon: 'connected_tv'
37 | }, {
38 | name: 'api',
39 | composition: 'backend'
40 | }],
41 | edges: [{
42 | name: 'e1',
43 | from: 'user',
44 | to: 'spa'
45 | }, {
46 | name: 'e2',
47 | from: 'spa',
48 | to: 'api'
49 | }],
50 | compositions: [{
51 | name: 'backend'
52 | }]
53 | };
54 | const graph: dagre.graphlib.Graph = DagreUtils.modelToGraph(model);
55 | expect(graph.nodes().length).toBe(3 /* nodes */ + 1 /* composition */);
56 | expect(graph.edges().length).toBe(2);
57 | });
58 |
59 |
60 | });
61 | });
62 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/actor/data-center/layout/dagre-utils.ts:
--------------------------------------------------------------------------------
1 | import * as dagre from 'dagre';
2 | import {BaseInfo, Edge, GraphModel, Node} from './dagre-model';
3 |
4 |
5 | /**
6 | * WIKI: https://github.com/dagrejs/dagre/wiki
7 | */
8 | export class DagreUtils {
9 |
10 | public static modelToGraph(model: GraphModel): dagre.graphlib.Graph {
11 | const g = new dagre.graphlib.Graph({
12 | compound: true,
13 | multigraph: true
14 | });
15 |
16 | g.setGraph(this.getLayout(model))
17 |
18 | g.setDefaultEdgeLabel(function () {
19 | return {};
20 | });
21 |
22 | this.updateGraph(g, model);
23 |
24 | // console.log('DagreUtils.layout model', model);
25 | // console.log('DagreUtils.layout g', g);
26 | dagre.layout(g);
27 | // console.log('DagreUtils.layout result (g.nodes())', g.nodes());
28 | return g;
29 | }
30 |
31 | public static updateBaseInfo(g: dagre.graphlib.Graph, baseInfo: Array | undefined ) {
32 | if (baseInfo) {
33 | baseInfo.forEach((node: Node) => {
34 | g.setNode(node.name, {label: node.label, width: 18, height: 18});
35 | if (node.composition) {
36 | g.setParent(node.name, node.composition);
37 | }
38 | });
39 | }
40 | }
41 |
42 | public static updateEdges(g: dagre.graphlib.Graph, model: GraphModel) {
43 | if (model.edges) {
44 | model.edges.forEach((edge: Edge) => {
45 | g.setEdge(edge.from, edge.to, {name: edge.name});
46 | });
47 | }
48 | }
49 |
50 | public static updateGraph(g: dagre.graphlib.Graph, model: GraphModel) {
51 | this.updateBaseInfo(g, model.compositions);
52 | this.updateBaseInfo(g, model.nodes);
53 | this.updateEdges(g, model);
54 | }
55 |
56 | public static getLayout(model: GraphModel): any {
57 | const layout: dagre.GraphLabel | undefined = model.layout;
58 | return layout;
59 | }
60 |
61 | }
62 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/actor/data-center/layout/index.ts:
--------------------------------------------------------------------------------
1 | export * from './dagre-layout.component';
2 | export * from './dagre-node.component';
3 | export * from './dagre-edge.component';
4 | export * from './dagre-composition.component';
5 | export * from './dagre-yaml-parser.component';
6 | export * from './dagre-model';
7 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/actor/data-center/server/abstract-server-actor.ts:
--------------------------------------------------------------------------------
1 | import {Directive, EventEmitter, Input, Output} from '@angular/core';
2 | import {AbstractEmptyDirective} from '../../../object';
3 |
4 | @Directive()
5 | export abstract class AbstractServerActor extends AbstractEmptyDirective {
6 |
7 | @Input()
8 | label!: string;
9 |
10 | @Output()
11 | render = new EventEmitter();
12 |
13 | @Output()
14 | selected = new EventEmitter();
15 |
16 | @Output()
17 | deselected = new EventEmitter();
18 |
19 | @Output()
20 | actorClick = new EventEmitter();
21 |
22 | @Input()
23 | icon?: string;
24 |
25 | color: string | number = '#ffffff';
26 |
27 | @Input()
28 | showFrame = true;
29 |
30 | public onSelected() {
31 | // console.log('ServerActorComponent.onSelected');
32 | this.color = '#fff0f0';
33 | }
34 |
35 | public onDeselected() {
36 | // console.log('ServerActorComponent.onDeselected');
37 | this.color = '#ffffff';
38 | }
39 |
40 | public onClick() {
41 | console.log('ServerActorComponent.onClick');
42 | this.color = '#ffa0a0';
43 | this.actorClick.emit();
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/actor/data-center/server/index.ts:
--------------------------------------------------------------------------------
1 | export * from './abstract-server-actor';
2 | export * from './server-barrel-actor.component';
3 | export * from './server-stand-actor.component';
4 | export * from './server-compact-actor.component';
5 | export * from './server-icon-actor.component';
6 | export * from './workstation-actor.component';
7 | export * from './model-actor.component';
8 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/actor/data-center/server/model-actor.component.ts:
--------------------------------------------------------------------------------
1 | import {Component, Input, Optional, SkipSelf} from '@angular/core';
2 | import {provideParent} from '../../../util';
3 | import {AbstractServerActor} from './abstract-server-actor';
4 | import {AbstractObject3D, ModelService} from '../../../object';
5 | import {RendererService} from '../../../renderer';
6 |
7 | @Component({
8 | selector: 'atft-model-actor',
9 | providers: [provideParent(ModelActorComponent)],
10 | template: `
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
20 |
21 |
23 |
24 |
25 |
26 | `
27 | })
28 | export class ModelActorComponent extends AbstractServerActor {
29 |
30 | @Input()
31 | set model(model: string) {
32 | const iconProvider = this.modelService.getSource(model);
33 | this.modelPath = iconProvider.url;
34 | }
35 |
36 | get model(): string {
37 | return this.model;
38 | }
39 |
40 | public modelPath!: string;
41 |
42 | constructor(
43 | protected override rendererService: RendererService,
44 | @SkipSelf() @Optional() protected override parent: AbstractObject3D,
45 | protected modelService: ModelService
46 | ) {
47 | super(rendererService, parent);
48 | }
49 |
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/actor/data-center/server/server-barrel-actor.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | import { provideParent } from '../../../util';
3 | import { AbstractServerActor } from './abstract-server-actor';
4 |
5 | @Component({
6 | selector: 'atft-server-barrel-actor',
7 | providers: [provideParent(ServerBarrelActorComponent)],
8 | template: `
9 |
10 |
11 |
12 |
13 |
15 |
16 |
17 |
20 |
21 |
22 |
23 |
25 |
26 |
28 |
29 |
30 | `
31 | })
32 | export class ServerBarrelActorComponent extends AbstractServerActor {
33 |
34 | textColor = '#DADADA';
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/actor/data-center/server/server-compact-actor.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | import { provideParent } from '../../../util';
3 | import { AbstractServerActor } from './abstract-server-actor';
4 |
5 | @Component({
6 | selector: 'atft-server-compact-actor',
7 | providers: [provideParent(ServerCompactActorComponent)],
8 | template: `
9 |
10 |
11 |
12 |
13 |
15 |
18 |
19 |
20 |
21 |
22 |
24 |
25 |
27 |
28 |
29 | `
30 | })
31 | export class ServerCompactActorComponent extends AbstractServerActor {
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/actor/data-center/server/server-icon-actor.component.ts:
--------------------------------------------------------------------------------
1 | import {Component} from '@angular/core';
2 | import {provideParent} from '../../../util';
3 | import {AbstractServerActor} from './abstract-server-actor';
4 |
5 | @Component({
6 | selector: 'atft-server-icon-actor',
7 | providers: [provideParent(ServerIconActorComponent)],
8 | template: `
9 |
10 |
11 |
12 |
15 |
16 |
17 |
18 |
20 |
21 |
23 |
24 |
25 |
26 | `
27 | })
28 | export class ServerIconActorComponent extends AbstractServerActor {
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/actor/data-center/server/server-stand-actor.component.spec.ts:
--------------------------------------------------------------------------------
1 | import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';
2 | import {AtftDataCenterActorModule} from '../atft-data-center-actor.module';
3 | import {RendererService} from '../../../renderer';
4 | import {AnimationService} from '../../../animation';
5 | import {ServerStandActorComponent} from './server-stand-actor.component';
6 | import {StatsService} from '../../../stats';
7 |
8 | describe('actor', () => {
9 | describe('ServerStandActorComponent', () => {
10 | let component: ServerStandActorComponent;
11 | let fixture: ComponentFixture;
12 |
13 | beforeEach(waitForAsync(() => {
14 | TestBed.configureTestingModule({
15 | imports: [
16 | AtftDataCenterActorModule
17 | ],
18 | providers: [
19 | StatsService,
20 | RendererService,
21 | AnimationService
22 | ]
23 | });
24 | fixture = TestBed.createComponent(ServerStandActorComponent);
25 | component = fixture.componentInstance;
26 | return TestBed.compileComponents();
27 | }));
28 |
29 |
30 | it('init', waitForAsync(() => {
31 | fixture.detectChanges();
32 | expect(component).toBeTruthy();
33 |
34 | component.onSelected();
35 | component.onDeselected();
36 | component.onClick();
37 |
38 | }));
39 |
40 |
41 | });
42 | });
43 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/actor/data-center/server/server-stand-actor.component.ts:
--------------------------------------------------------------------------------
1 | import {Component} from '@angular/core';
2 | import { provideParent } from '../../../util';
3 | import { AbstractServerActor } from './abstract-server-actor';
4 |
5 | @Component({
6 | selector: 'atft-server-stand-actor',
7 | providers: [provideParent(ServerStandActorComponent)],
8 | template: `
9 |
10 |
11 |
12 |
13 |
15 |
18 |
19 |
20 |
21 |
22 |
25 |
26 |
28 |
29 |
30 | `
31 | })
32 | export class ServerStandActorComponent extends AbstractServerActor {
33 |
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/actor/data-center/service/index.ts:
--------------------------------------------------------------------------------
1 | export * from './actor-repository.service';
2 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/actor/ux/index.ts:
--------------------------------------------------------------------------------
1 | export * from './ux-actor.module';
2 | export * from './text';
3 | export * from './loader';
4 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/actor/ux/loader/index.ts:
--------------------------------------------------------------------------------
1 | export * from './loader-actor.component';
2 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/actor/ux/loader/loader-actor.component.spec.ts:
--------------------------------------------------------------------------------
1 | import {ComponentFixture, TestBed} from '@angular/core/testing';
2 | import {StatsService} from '../../../stats';
3 | import {RendererService} from '../../../renderer';
4 | import {AnimationService} from '../../../animation';
5 | import {TextMeshComponent} from '../../../object';
6 | import {LoaderActorComponent} from './loader-actor.component';
7 |
8 |
9 | describe('ux', () => {
10 |
11 | describe('LoaderActorComponent', () => {
12 | let component: LoaderActorComponent;
13 | let fixture: ComponentFixture;
14 |
15 | beforeEach(() => {
16 | TestBed.configureTestingModule({
17 | declarations: [
18 | LoaderActorComponent,
19 | TextMeshComponent
20 | ],
21 | providers: [
22 | StatsService,
23 | RendererService,
24 | AnimationService
25 | ]
26 | });
27 | fixture = TestBed.createComponent(LoaderActorComponent);
28 | TestBed.compileComponents();
29 | });
30 |
31 | it('init', () => {
32 | component = fixture.componentInstance;
33 | component.animate = true;
34 | component.materialColor = '#ff0000';
35 |
36 | fixture.autoDetectChanges(true);
37 | fixture.detectChanges();
38 |
39 | expect(component).toBeTruthy();
40 | });
41 |
42 | });
43 |
44 | });
45 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/actor/ux/text/index.ts:
--------------------------------------------------------------------------------
1 | export * from './text-actor.component';
2 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/actor/ux/text/text-actor.component.spec.ts:
--------------------------------------------------------------------------------
1 | import {TextActorComponent} from './text-actor.component';
2 | import {ComponentFixture, TestBed} from '@angular/core/testing';
3 | import {StatsService} from '../../../stats';
4 | import {RendererService} from '../../../renderer';
5 | import {AnimationService} from '../../../animation';
6 | import {FontService, TextMeshComponent} from '../../../object';
7 |
8 |
9 | describe('ux', () => {
10 |
11 | describe('TextActorComponent', () => {
12 | let component: TextActorComponent;
13 | let fixture: ComponentFixture;
14 |
15 | beforeEach(() => {
16 | TestBed.configureTestingModule({
17 | declarations: [
18 | TextActorComponent,
19 | TextMeshComponent
20 | ],
21 | providers: [
22 | StatsService,
23 | RendererService,
24 | AnimationService,
25 | FontService
26 | ]
27 | });
28 | fixture = TestBed.createComponent(TextActorComponent);
29 | TestBed.compileComponents();
30 | });
31 |
32 | it('init', () => {
33 | component = fixture.componentInstance;
34 | component.text = 'Sample Text';
35 | component.currentText = '';
36 | component.animate = true;
37 | component.materialColor = '#ff0000';
38 |
39 | fixture.detectChanges();
40 |
41 | expect(component).toBeTruthy();
42 | expect(component.currentText).toBe('');
43 | });
44 |
45 | });
46 |
47 | });
48 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/actor/ux/ux-actor.module.ts:
--------------------------------------------------------------------------------
1 | import {NgModule} from '@angular/core';
2 | import {CommonModule} from '@angular/common';
3 | import {AtftModule} from '../../atft.module';
4 | import {TextActorComponent} from './text/text-actor.component';
5 | import {LoaderActorComponent} from './loader';
6 |
7 | @NgModule({
8 | imports: [
9 | CommonModule,
10 | AtftModule
11 | ],
12 | declarations: [
13 | TextActorComponent,
14 | LoaderActorComponent
15 | ],
16 | exports: [
17 | TextActorComponent,
18 | LoaderActorComponent
19 | ]
20 | })
21 | export class UxActorModule {
22 | }
23 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/animation/animation.service.spec.ts:
--------------------------------------------------------------------------------
1 | import {AnimationService} from './animation.service';
2 | import {RendererService} from '../renderer';
3 | import {fakeAsync, tick} from '@angular/core/testing';
4 | import {StatsService} from '../stats';
5 |
6 | describe('animation', () => {
7 | describe('AnimationService', () => {
8 |
9 |
10 | it('validate', fakeAsync(() => {
11 | const stats = new StatsService();
12 | const renderer = new RendererService(stats);
13 | const animation = new AnimationService(renderer);
14 |
15 | let called = false;
16 | animation.animate.subscribe(() => {
17 | called = true;
18 | });
19 | spyOn(window, 'requestAnimationFrame').and.callThrough();
20 | animation.start();
21 |
22 | tick();
23 | animation.animationStep();
24 | tick();
25 | animation.stop();
26 |
27 | expect(window.requestAnimationFrame).toHaveBeenCalled();
28 | expect(called).toBeTruthy();
29 | }));
30 |
31 |
32 | });
33 |
34 | });
35 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/animation/atft-animation.module.ts:
--------------------------------------------------------------------------------
1 | import {NgModule} from '@angular/core';
2 | import {AnimationService} from './animation.service';
3 | import {EmitStepComponent, TimelineEmitterComponent} from "./timeline-emitter";
4 |
5 |
6 | @NgModule({
7 | declarations: [
8 | TimelineEmitterComponent,
9 | EmitStepComponent,
10 | ],
11 | providers: [
12 | AnimationService,
13 | ],
14 | exports: [
15 | TimelineEmitterComponent,
16 | EmitStepComponent,
17 | ],
18 | })
19 | export class AtftAnimationModule {
20 | }
21 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/animation/index.ts:
--------------------------------------------------------------------------------
1 | export * from './atft-animation.module';
2 | export * from './animation.service';
3 | export * from './timeline-emitter';
4 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/animation/timeline-emitter/emit-step.component.ts:
--------------------------------------------------------------------------------
1 | import {
2 | AfterViewInit,
3 | Component,
4 | EventEmitter,
5 | Input,
6 | OnChanges,
7 | OnDestroy,
8 | Output,
9 | SimpleChanges
10 | } from '@angular/core';
11 | import {provideParent} from "../../util";
12 |
13 | @Component({
14 | selector: 'atft-emit-step',
15 | providers: [provideParent(EmitStepComponent)],
16 | template: ''
17 | })
18 | export class EmitStepComponent implements AfterViewInit, OnDestroy, OnChanges {
19 |
20 | @Input() ms!: number;
21 | @Output() action = new EventEmitter();
22 |
23 |
24 | constructor() {
25 | console.log('EmitStepComponent.constructor');
26 | }
27 |
28 | public ngAfterViewInit() {
29 | console.log('EmitStepComponent.ngAfterViewInit');
30 | }
31 |
32 | ngOnDestroy(): void {
33 | console.log('EmitStepComponent.ngOnDestroy');
34 | }
35 |
36 | public ngOnChanges(changes: SimpleChanges) {
37 | console.log('EmitStepComponent.ngOnChanges', changes);
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/animation/timeline-emitter/index.ts:
--------------------------------------------------------------------------------
1 | export * from './timeline-emitter.component'
2 | export * from './emit-step.component'
3 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/animation/timeline-emitter/timeline-emitter.component.ts:
--------------------------------------------------------------------------------
1 | import {
2 | AfterViewInit,
3 | Component,
4 | ContentChildren,
5 | Input,
6 | OnChanges,
7 | OnDestroy,
8 | OnInit,
9 | QueryList,
10 | SimpleChanges
11 | } from '@angular/core';
12 | import {EmitStepComponent} from "./emit-step.component";
13 |
14 | @Component({
15 | selector: 'atft-timeline-emitter',
16 | template: ''
17 | })
18 | export class TimelineEmitterComponent implements AfterViewInit, OnDestroy, OnChanges, OnInit {
19 |
20 | @ContentChildren(EmitStepComponent, {descendants: true}) emitSteps!: QueryList;
21 | @Input() autostart = false;
22 |
23 | constructor() {
24 | console.log('TimelineEmitterComponent.constructor');
25 | }
26 |
27 | public ngAfterViewInit() {
28 | console.log('TimelineEmitterComponent.ngAfterViewInit', this.emitSteps);
29 | if (this.autostart) {
30 | this.start()
31 | }
32 | }
33 |
34 | ngOnDestroy(): void {
35 | console.log('TimelineEmitterComponent.ngOnDestroy');
36 | }
37 |
38 | public ngOnChanges(changes: SimpleChanges) {
39 | console.log('TimelineEmitterComponent.ngOnChanges', changes);
40 | }
41 |
42 | ngOnInit(): void {
43 | console.log('TimelineEmitterComponent.ngOnInit', this.emitSteps);
44 | }
45 |
46 | public start() {
47 | console.log('TimelineEmitterComponent.start');
48 | this.emitSteps?.forEach(i => {
49 | console.log('TimelineEmitterComponent.start setTimeout on', i.ms);
50 | setTimeout(() => {
51 | i.action.emit()
52 | }, i.ms)
53 | })
54 | }
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/atft.module.ts:
--------------------------------------------------------------------------------
1 | import {NgModule} from '@angular/core';
2 | import {AtftCameraModule} from './camera/atft-camera.module';
3 | import {AtftObjectModule} from './object/atft-object.module';
4 | import {AtftControlModule} from './control/atft-control.module';
5 | import {AtftPipeModule} from './pipe/atft-pipe.module';
6 | import {AtftRendererModule} from './renderer/atft-renderer.module';
7 | import {AtftAnimationModule} from './animation/atft-animation.module';
8 | import {AtftRaycasterModule} from './raycaster/atft-raycaster.module';
9 | import {AtftStatsModule} from './stats/stats.module';
10 | import {AtftEffectModule} from './effect/atft-effect.module';
11 |
12 | // NOTE: In case of "ERROR in Unexpected value 'undefined' exported by the module 'AtftModule" fix imports (do not import index.ts)
13 |
14 | @NgModule({
15 | imports: [
16 | AtftCameraModule,
17 | AtftObjectModule,
18 | AtftControlModule,
19 | AtftPipeModule,
20 | AtftRendererModule,
21 | AtftAnimationModule,
22 | AtftRaycasterModule,
23 | AtftStatsModule,
24 | AtftEffectModule
25 | ],
26 | exports: [
27 | AtftCameraModule,
28 | AtftObjectModule,
29 | AtftControlModule,
30 | AtftPipeModule,
31 | AtftRendererModule,
32 | AtftAnimationModule,
33 | AtftRaycasterModule,
34 | AtftStatsModule,
35 | AtftEffectModule
36 | ]
37 | })
38 | export class AtftModule {
39 | }
40 |
41 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/camera/atft-camera.module.ts:
--------------------------------------------------------------------------------
1 | import {NgModule} from '@angular/core';
2 | import {CommonModule} from '@angular/common';
3 | import {PerspectiveCameraComponent} from './perspective-camera.component';
4 | import {OrthographicCameraComponent} from './orthographic-camera.component';
5 |
6 | @NgModule({
7 | imports: [
8 | CommonModule
9 | ],
10 | declarations: [
11 | PerspectiveCameraComponent,
12 | OrthographicCameraComponent
13 | ],
14 | exports: [
15 | PerspectiveCameraComponent,
16 | OrthographicCameraComponent
17 | ]
18 | })
19 | export class AtftCameraModule {
20 | }
21 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/camera/index.ts:
--------------------------------------------------------------------------------
1 | export * from './atft-camera.module';
2 | export * from './abstract-camera';
3 | export * from './perspective-camera.component';
4 | export * from './orthographic-camera.component';
5 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/camera/perspective-camera.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, Input } from '@angular/core';
2 | import * as THREE from 'three';
3 | import { RendererService } from '../renderer/renderer.service';
4 | import { provideParent } from '../util';
5 | import { AbstractCamera } from './abstract-camera';
6 |
7 | @Component({
8 | selector: 'atft-perspective-camera',
9 | providers: [provideParent(PerspectiveCameraComponent, AbstractCamera)],
10 | template: ''
11 | })
12 | export class PerspectiveCameraComponent extends AbstractCamera {
13 |
14 | @Input() fov!: number;
15 | @Input() near!: number;
16 | @Input() far!: number;
17 |
18 | constructor(
19 | protected override rendererService: RendererService
20 | ) {
21 | super(rendererService);
22 | }
23 |
24 | protected createCamera(): void {
25 | // console.log('PerspectiveCameraComponent.createCamera');
26 | // let aspectRatio = undefined; // Updated later
27 | this.camera = new THREE.PerspectiveCamera(
28 | this.fov,
29 | undefined,
30 | this.near,
31 | this.far
32 | );
33 | }
34 |
35 | public updateAspectRatio(aspect: number) {
36 | // console.log('PerspectiveCameraComponent.updateAspectRatio: ' + aspect);
37 | this.camera.aspect = aspect;
38 | this.camera.updateProjectionMatrix();
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/control/atft-control.module.ts:
--------------------------------------------------------------------------------
1 | import {NgModule} from '@angular/core';
2 | import {CommonModule} from '@angular/common';
3 | import {OrbitControlsComponent} from './orbit-controls.component';
4 | import {MapControlsComponent} from './map-controls.component';
5 |
6 |
7 | @NgModule({
8 | declarations: [
9 | OrbitControlsComponent,
10 | MapControlsComponent
11 | ],
12 | imports: [
13 | CommonModule
14 | ],
15 | exports: [
16 | OrbitControlsComponent,
17 | MapControlsComponent
18 | ]
19 | })
20 | export class AtftControlModule {
21 | }
22 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/control/controls.component.scss:
--------------------------------------------------------------------------------
1 | :host {
2 | display: flex;
3 | flex: 1;
4 | height: 100%;
5 | }
6 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/control/index.ts:
--------------------------------------------------------------------------------
1 | export * from './atft-control.module';
2 | export * from './orbit-controls.component';
3 | export * from './map-controls.component';
4 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/control/orbit-controls.component.ts:
--------------------------------------------------------------------------------
1 | import {Component, Input, OnChanges, SimpleChanges} from '@angular/core';
2 | import {OrbitControls} from 'three/examples/jsm/controls/OrbitControls';
3 | import {RendererService} from '../renderer/renderer.service';
4 | import {RaycasterService} from '../raycaster/raycaster.service';
5 | import {AbstractOrbitControls} from './abstract-orbit-controls';
6 |
7 | @Component({
8 | selector: 'atft-orbit-controls',
9 | template: `
10 | `,
11 | styleUrls: ['controls.component.scss']
12 | })
13 | export class OrbitControlsComponent extends AbstractOrbitControls implements OnChanges {
14 |
15 | @Input() rotateSpeed = 1.0;
16 | @Input() zoomSpeed = 1.2;
17 |
18 | constructor(
19 | protected override rendererService: RendererService,
20 | protected override raycasterService: RaycasterService
21 | ) {
22 | super(rendererService, raycasterService);
23 | }
24 |
25 | protected setUpControls() {
26 | this.controls = new OrbitControls(
27 | this.childCameras.first.camera,
28 | this.listeningControlElement && this.listeningControlElement.nativeElement
29 | );
30 | this.controls.rotateSpeed = this.rotateSpeed;
31 | this.controls.zoomSpeed = this.zoomSpeed;
32 | }
33 |
34 | override ngOnChanges(changes: SimpleChanges) {
35 | if (!this.controls) {
36 | return;
37 | }
38 | super.ngOnChanges(changes);
39 |
40 | if (changes['rotateSpeed']) {
41 | this.controls.rotateSpeed = this.rotateSpeed;
42 | }
43 | if (changes['zoomSpeed']) {
44 | this.controls.zoomSpeed = this.zoomSpeed;
45 | }
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/effect/atft-effect.module.ts:
--------------------------------------------------------------------------------
1 | import {NgModule} from '@angular/core';
2 | import {CommonModule} from '@angular/common';
3 | import {DashedDrawDirective} from './dashed-draw.directive';
4 | import {FogComponent} from './fog.component';
5 | import {DofComponent} from './compose/dof.component';
6 | import {DotScreenComponent} from './compose/dot-screen.component';
7 | import {EffectComposerComponent} from './compose/effect-composer.component';
8 | import {BlurComponent} from './compose/blur.component';
9 |
10 |
11 | @NgModule({
12 | imports: [
13 | CommonModule
14 | ],
15 | declarations: [
16 | DashedDrawDirective,
17 | FogComponent,
18 | DofComponent,
19 | DotScreenComponent,
20 | EffectComposerComponent,
21 | BlurComponent
22 | ],
23 | exports: [
24 | DashedDrawDirective,
25 | FogComponent,
26 | DofComponent,
27 | DotScreenComponent,
28 | EffectComposerComponent,
29 | BlurComponent
30 | ]
31 | })
32 | export class AtftEffectModule {
33 | }
34 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/effect/compose/abstract-compose-effect.ts:
--------------------------------------------------------------------------------
1 | import {AfterViewInit, Directive, OnChanges, OnDestroy, Optional, SimpleChanges, SkipSelf} from '@angular/core';
2 | import {RendererService} from '../../renderer/renderer.service';
3 | import {EffectComposerComponent} from './effect-composer.component';
4 | import {Pass} from 'three/examples/jsm/postprocessing/Pass';
5 |
6 | @Directive()
7 | export abstract class AbstractComposeEffect implements AfterViewInit, OnDestroy, OnChanges {
8 |
9 | protected pass: T[] = [];
10 |
11 | constructor(
12 | protected rendererService: RendererService,
13 | @SkipSelf() @Optional() protected composer: EffectComposerComponent
14 | ) {
15 | // console.log('AbstractComposeEffect.constructor', parentScene);
16 | }
17 |
18 | protected enable() {
19 | if (this.composer) {
20 | // console.log('AbstractComposeEffect.enable', this.rendererService);
21 |
22 | this.initPasses();
23 |
24 | this.pass.forEach(pass => this.composer.addPass(pass));
25 | this.rendererService.render();
26 | }
27 | }
28 |
29 | protected disable() {
30 | if (this.composer) {
31 | // console.log('AbstractComposeEffect.disable');
32 | this.pass.forEach(pass => this.composer.removePass(pass));
33 | this.rendererService.render();
34 | }
35 | }
36 |
37 | public ngAfterViewInit() {
38 | this.enable();
39 | }
40 |
41 | ngOnDestroy(): void {
42 | this.disable();
43 | }
44 |
45 | public ngOnChanges(changes: SimpleChanges) {
46 | if (!this.pass) {
47 | return;
48 | }
49 | if (this.applyChanges(changes)) {
50 | this.rendererService.render();
51 | }
52 | }
53 |
54 | abstract initPasses() : void;
55 |
56 | abstract applyChanges(changes: SimpleChanges): boolean;
57 |
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/effect/compose/blur.component.ts:
--------------------------------------------------------------------------------
1 | import {Component, Optional, SimpleChanges, SkipSelf} from '@angular/core';
2 | import {RendererService} from '../../renderer/renderer.service';
3 | import {ShaderPass} from 'three/examples/jsm/postprocessing/ShaderPass';
4 | import {VerticalBlurShader} from 'three/examples/jsm/shaders/VerticalBlurShader';
5 | import {HorizontalBlurShader} from 'three/examples/jsm/shaders/HorizontalBlurShader';
6 | import {EffectComposerComponent} from './effect-composer.component';
7 | import {AbstractComposeEffect} from './abstract-compose-effect';
8 |
9 | @Component({
10 | selector: 'atft-blur',
11 | template: ''
12 | })
13 | export class BlurComponent extends AbstractComposeEffect {
14 |
15 | constructor(
16 | protected override rendererService: RendererService,
17 | @SkipSelf() @Optional() protected override composer: EffectComposerComponent
18 | ) {
19 | super(rendererService, composer);
20 | }
21 |
22 | initPasses() {
23 | this.pass.push(new ShaderPass(VerticalBlurShader));
24 | this.pass.push(new ShaderPass(HorizontalBlurShader));
25 | this.pass.push(new ShaderPass(VerticalBlurShader));
26 | this.pass.push(new ShaderPass(HorizontalBlurShader));
27 | }
28 |
29 | // eslint-disable-next-line @typescript-eslint/no-unused-vars
30 | applyChanges(changes: SimpleChanges) {
31 | // TODO: implement changes
32 | return false;
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/effect/compose/dot-screen.component.ts:
--------------------------------------------------------------------------------
1 | import {Component, Input, Optional, SimpleChanges, SkipSelf} from '@angular/core';
2 | import {RendererService} from '../../renderer/renderer.service';
3 | import {EffectComposerComponent} from './effect-composer.component';
4 | import {ShaderPass} from 'three/examples/jsm/postprocessing/ShaderPass';
5 | import {DotScreenShader} from 'three/examples/jsm/shaders/DotScreenShader';
6 | import {AbstractComposeEffect} from './abstract-compose-effect';
7 |
8 | @Component({
9 | selector: 'atft-dot-screen',
10 | template: ''
11 | })
12 | export class DotScreenComponent extends AbstractComposeEffect {
13 |
14 | @Input() scale = 4;
15 |
16 | constructor(
17 | protected override rendererService: RendererService,
18 | @SkipSelf() @Optional() protected override composer: EffectComposerComponent
19 | ) {
20 | super(rendererService, composer);
21 | }
22 |
23 | initPasses() {
24 | this.pass.push(new ShaderPass(DotScreenShader));
25 | }
26 |
27 | // eslint-disable-next-line @typescript-eslint/no-unused-vars
28 | applyChanges(changes: SimpleChanges): boolean {
29 | // TODO: Implement
30 | return false;
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/effect/compose/effect-composer.component.spec.ts:
--------------------------------------------------------------------------------
1 | import {Component} from '@angular/core';
2 | import {ComponentFixture, TestBed} from '@angular/core/testing';
3 | import {StatsService} from '../../stats';
4 | import {RendererService} from '../../renderer';
5 | import {AnimationService} from '../../animation';
6 | import {EffectComposerComponent} from './effect-composer.component';
7 | import {BlurComponent} from './blur.component';
8 |
9 |
10 | @Component({
11 | template: `
12 |
13 |
14 | `
15 | })
16 | export class MockComponent { }
17 |
18 | describe('effect', () => {
19 |
20 | describe('EffectComposerComponent', () => {
21 | let component: MockComponent;
22 | let fixture: ComponentFixture;
23 |
24 | beforeEach(() => {
25 | TestBed.configureTestingModule({
26 | declarations: [
27 | MockComponent,
28 | EffectComposerComponent,
29 | BlurComponent
30 | ],
31 | providers: [
32 | StatsService,
33 | RendererService,
34 | AnimationService,
35 | ]
36 | });
37 | fixture = TestBed.createComponent(MockComponent);
38 | TestBed.compileComponents();
39 | });
40 |
41 | it('init', () => {
42 | component = fixture.componentInstance;
43 |
44 | fixture.detectChanges();
45 | expect(component).toBeTruthy();
46 | });
47 |
48 | });
49 |
50 | });
51 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/effect/dashed-draw.directive.spec.ts:
--------------------------------------------------------------------------------
1 | import {ComponentFixture, TestBed} from '@angular/core/testing';
2 | import {DashedDrawDirective} from './dashed-draw.directive';
3 | import {StatsService} from '../stats';
4 | import {RendererService} from '../renderer';
5 | import {AnimationService} from '../animation';
6 | import {Component} from '@angular/core';
7 | import {BoxMeshComponent} from '../object';
8 |
9 |
10 | @Component({
11 | template: `
12 |
13 | `
14 | })
15 | export class MockComponent { }
16 |
17 | describe('effect', () => {
18 |
19 | describe('DashedDrawDirective', () => {
20 | let component: MockComponent;
21 | let fixture: ComponentFixture;
22 |
23 | beforeEach(() => {
24 | TestBed.configureTestingModule({
25 | declarations: [
26 | DashedDrawDirective,
27 | MockComponent,
28 | BoxMeshComponent
29 | ],
30 | providers: [
31 | StatsService,
32 | RendererService,
33 | AnimationService,
34 | ]
35 | });
36 | fixture = TestBed.createComponent(MockComponent);
37 | TestBed.compileComponents();
38 | });
39 |
40 | it('init', () => {
41 | component = fixture.componentInstance;
42 |
43 | fixture.detectChanges();
44 | expect(component).toBeTruthy();
45 | });
46 |
47 | });
48 |
49 | });
50 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/effect/fog.component.ts:
--------------------------------------------------------------------------------
1 | import {AfterViewInit, Component, Input, OnChanges, OnDestroy, Optional, SimpleChanges, SkipSelf} from '@angular/core';
2 | import {RendererService} from '../renderer/renderer.service';
3 | import {SceneComponent} from '../object';
4 | import * as THREE from 'three';
5 |
6 | @Component({
7 | selector: 'atft-fog',
8 | template: ''
9 | })
10 | export class FogComponent implements AfterViewInit, OnDestroy, OnChanges {
11 |
12 | @Input() color: string | number = '#0000AA';
13 | @Input() near = 10;
14 | @Input() far = 100;
15 |
16 | protected scene!: THREE.Scene;
17 |
18 | constructor(
19 | protected rendererService: RendererService,
20 | @SkipSelf() @Optional() protected parent: SceneComponent
21 | ) {
22 | // console.log('FogComponent.constructor', parentScene);
23 |
24 | }
25 |
26 |
27 | public ngAfterViewInit() {
28 | // console.log('ngAfterViewInit', this.parentScene);
29 | if (this.parent && this.parent.getObject() && this.parent.getObject().isScene) {
30 | // console.log('FogComponent detected parentScene scene', this.parentScene);
31 | this.scene = this.parent.getObject();
32 | this.enableFog();
33 | }
34 | }
35 |
36 | protected enableFog() {
37 | // console.log('enableFog');
38 | this.scene.fog = new THREE.Fog(this.color, this.near, this.far);
39 | this.rendererService.render();
40 | }
41 |
42 | protected disableFog() {
43 | // console.log('disableFog');
44 | this.scene.fog = null;
45 | this.rendererService.render();
46 | }
47 |
48 | ngOnDestroy(): void {
49 | this.disableFog();
50 | }
51 |
52 |
53 | public ngOnChanges(changes: SimpleChanges) {
54 | if (!this.scene) {
55 | return;
56 | }
57 |
58 | if (['color', 'near', 'far'].some(propName => propName in changes)) {
59 | this.enableFog();
60 | }
61 |
62 | }
63 |
64 | }
65 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/effect/index.ts:
--------------------------------------------------------------------------------
1 | export * from './atft-effect.module';
2 | export * from './dashed-draw.directive';
3 | export * from './fog.component';
4 | export * from './compose/dof.component';
5 | export * from './compose/dot-screen.component';
6 | export * from './compose/effect-composer.component';
7 | export * from './compose/blur.component';
8 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/object/abstract-empty-directive.ts:
--------------------------------------------------------------------------------
1 | import { Directive } from '@angular/core';
2 | import * as THREE from 'three';
3 | import { AbstractObject3D } from './abstract-object-3d';
4 |
5 | @Directive()
6 | export abstract class AbstractEmptyDirective extends AbstractObject3D {
7 |
8 | protected newObject3DInstance(): THREE.Object3D {
9 | return new THREE.Object3D();
10 | }
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/object/atft-object.module.ts:
--------------------------------------------------------------------------------
1 | import {NgModule} from '@angular/core';
2 | import {AtftConnectorModule} from './connector/atft-connector.module';
3 | import {AtftHelperModule} from './helper/atft-helper.module';
4 | import {AtftLightModule} from './light/atft-light.module';
5 | import {AtftLoaderModule} from './loader/atft-loader.module';
6 | import {AtftMeshModule} from './mesh/atft-mesh.module';
7 | import {AtftTextModule} from './text/atft-text.module';
8 | import {CommonModule} from '@angular/common';
9 | import {SceneComponent} from './scene.component';
10 | import {ContentProjectionComponent} from './content-projection.component';
11 | import {FontService, IconService, ModelService, ObjLoaderService, SvgLoaderService} from './loader';
12 |
13 |
14 | @NgModule({
15 | imports: [
16 | CommonModule,
17 | AtftConnectorModule,
18 | AtftHelperModule,
19 | AtftLightModule,
20 | AtftLoaderModule,
21 | AtftMeshModule,
22 | AtftTextModule
23 | ],
24 | declarations: [
25 | SceneComponent,
26 | ContentProjectionComponent
27 | ],
28 | exports: [
29 | SceneComponent,
30 | AtftConnectorModule,
31 | AtftHelperModule,
32 | AtftLightModule,
33 | AtftLoaderModule,
34 | AtftMeshModule,
35 | AtftTextModule
36 | ], providers: [
37 | FontService,
38 | SvgLoaderService,
39 | IconService,
40 | ObjLoaderService,
41 | ModelService
42 | ]
43 | })
44 | export class AtftObjectModule {
45 | }
46 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/object/connector/abstract-connector.ts:
--------------------------------------------------------------------------------
1 | import {Directive, Input, OnDestroy} from '@angular/core';
2 | import * as THREE from 'three';
3 | import {AbstractObject3D} from '../abstract-object-3d';
4 | import {Subscription} from 'rxjs';
5 |
6 | @Directive()
7 | export abstract class AbstractConnector extends AbstractObject3D implements OnDestroy {
8 |
9 | @Input()
10 | source!: AbstractObject3D;
11 |
12 | @Input()
13 | target!: AbstractObject3D;
14 |
15 | protected sourceSub!: Subscription;
16 | protected targetSub!: Subscription;
17 |
18 | protected newObject3DInstance(): THREE.Object3D {
19 | const line = this.createLineMesh();
20 | if (this.source && this.target) {
21 | this.watchObjects();
22 | }
23 | return line;
24 | }
25 |
26 | private watchObjects() {
27 | this.sourceSub = this.source.changed.subscribe(() => {
28 | this.updateLineGeometry();
29 | });
30 |
31 | this.targetSub = this.target.changed.subscribe(() => {
32 | this.updateLineGeometry();
33 | });
34 | }
35 |
36 | public override ngOnDestroy() {
37 | super.ngOnDestroy();
38 |
39 | this.sourceSub?.unsubscribe();
40 | this.targetSub?.unsubscribe();
41 | }
42 |
43 | /**
44 | * Create line mesh
45 | */
46 | abstract createLineMesh(): T;
47 |
48 | /**
49 | * If at least one line end (source or target object) changed, then line geoetry should be updated as well
50 | * // TODO: Calculate only when source/target positions were changed
51 | */
52 | abstract updateLineGeometry(): void;
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/object/connector/atft-connector.module.ts:
--------------------------------------------------------------------------------
1 | import {NgModule} from '@angular/core';
2 | import {CommonModule} from '@angular/common';
3 | import {LineConnectorComponent} from './line-connector.component';
4 |
5 |
6 | @NgModule({
7 | declarations: [
8 | LineConnectorComponent
9 | ],
10 | imports: [
11 | CommonModule
12 | ],
13 | exports: [
14 | LineConnectorComponent
15 | ]
16 | })
17 | export class AtftConnectorModule {
18 | }
19 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/object/connector/index.ts:
--------------------------------------------------------------------------------
1 | export * from './atft-connector.module';
2 | export * from './line-connector.component';
3 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/object/content-projection.component.ts:
--------------------------------------------------------------------------------
1 | import {Component, Optional, SkipSelf, ViewChild} from '@angular/core';
2 | import {EmptyComponent} from './helper/empty.component';
3 | import {AbstractObject3D} from './abstract-object-3d';
4 | import {RendererService} from '../renderer/renderer.service';
5 | import {provideParent} from '../util';
6 |
7 | @Component({
8 | selector: 'atft-content-projection',
9 | providers: [provideParent(ContentProjectionComponent)],
10 | template: ''
11 | })
12 | export class ContentProjectionComponent extends EmptyComponent {
13 |
14 | @ViewChild('contentProjection') contentProjection!: AbstractObject3D;
15 |
16 | constructor(
17 | protected override rendererService: RendererService,
18 | @SkipSelf() @Optional() protected override parent: AbstractObject3D
19 | ) {
20 | super(rendererService, parent);
21 | }
22 |
23 | override addChild(object: AbstractObject3D) {
24 | if (this.contentProjection) {
25 | this.contentProjection.addChild(object);
26 | } else {
27 | console.error('ContentProjectionComponent error: #contentProjection name not found! Embedded child object in "ng-content" can not be attached to parentScene object');
28 | }
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/object/helper/atft-helper.module.ts:
--------------------------------------------------------------------------------
1 | import {NgModule} from '@angular/core';
2 | import {CommonModule} from '@angular/common';
3 | import {AxesHelperComponent} from './axes-helper.component';
4 | import {EmptyComponent} from './empty.component';
5 | import {GridHelperComponent} from './grid-helper.component';
6 |
7 |
8 | @NgModule({
9 | declarations: [
10 | AxesHelperComponent,
11 | EmptyComponent,
12 | GridHelperComponent
13 | ],
14 | imports: [
15 | CommonModule
16 | ],
17 | exports: [
18 | AxesHelperComponent,
19 | EmptyComponent,
20 | GridHelperComponent
21 | ]
22 | })
23 | export class AtftHelperModule {
24 | }
25 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/object/helper/axes-helper.component.spec.ts:
--------------------------------------------------------------------------------
1 | import {RendererService} from '../../renderer/renderer.service';
2 | import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';
3 | import {StatsService} from '../../stats';
4 | import {AxesHelperComponent} from './axes-helper.component';
5 |
6 | describe('helper', () => {
7 | describe('AxesHelperComponent', () => {
8 | let component: AxesHelperComponent;
9 | let fixture: ComponentFixture;
10 |
11 | beforeEach(waitForAsync(() => {
12 | TestBed.configureTestingModule({
13 | declarations: [
14 | AxesHelperComponent
15 | ],
16 | providers: [
17 | StatsService,
18 | RendererService
19 | ]
20 | });
21 | fixture = TestBed.createComponent(AxesHelperComponent);
22 | component = fixture.componentInstance;
23 | return TestBed.compileComponents();
24 | }));
25 |
26 | it('init', () => {
27 | fixture.detectChanges();
28 | expect(component).toBeTruthy();
29 | });
30 |
31 | });
32 | });
33 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/object/helper/axes-helper.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, Input, Optional, SkipSelf } from '@angular/core';
2 | import * as THREE from 'three';
3 | import { RendererService } from '../../renderer/renderer.service';
4 | import { provideParent } from '../../util';
5 | import { AbstractObject3D } from '../abstract-object-3d';
6 |
7 | @Component({
8 | selector: 'atft-axes-helper',
9 | providers: [provideParent(AxesHelperComponent)],
10 | template: ''
11 | })
12 | export class AxesHelperComponent extends AbstractObject3D {
13 |
14 | @Input() size = 50;
15 |
16 | constructor(
17 | protected override rendererService: RendererService,
18 | @SkipSelf() @Optional() protected override parent: AbstractObject3D
19 | ) {
20 | super(rendererService, parent);
21 | }
22 |
23 | protected newObject3DInstance(): THREE.AxesHelper {
24 | // console.log('AxesHelperComponent.newObject3DInstance');
25 | return new THREE.AxesHelper(this.size);
26 | }
27 |
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/object/helper/empty.component.ts:
--------------------------------------------------------------------------------
1 | import { AfterViewInit, Component, Optional, SkipSelf } from '@angular/core';
2 | import * as THREE from 'three';
3 | import { RendererService } from '../../renderer/renderer.service';
4 | import { provideParent } from '../../util';
5 | import { AbstractObject3D } from '../abstract-object-3d';
6 |
7 | @Component({
8 | selector: 'atft-empty',
9 | providers: [provideParent(EmptyComponent)],
10 | template: ''
11 | })
12 | export class EmptyComponent extends AbstractObject3D implements AfterViewInit {
13 |
14 | constructor(
15 | protected override rendererService: RendererService,
16 | @SkipSelf() @Optional() protected override parent: AbstractObject3D
17 | ) {
18 | super(rendererService, parent);
19 | }
20 |
21 | protected newObject3DInstance(): THREE.Object3D {
22 | return new THREE.Object3D();
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/object/helper/grid-helper.component.spec.ts:
--------------------------------------------------------------------------------
1 | import {RendererService} from '../../renderer/renderer.service';
2 | import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';
3 | import {StatsService} from '../../stats';
4 | import {GridHelperComponent} from './grid-helper.component';
5 |
6 | describe('helper', () => {
7 | describe('GridHelperComponent', () => {
8 | let component: GridHelperComponent;
9 | let fixture: ComponentFixture;
10 |
11 | beforeEach(waitForAsync(() => {
12 | TestBed.configureTestingModule({
13 | declarations: [
14 | GridHelperComponent
15 | ],
16 | providers: [
17 | StatsService,
18 | RendererService
19 | ]
20 | });
21 | fixture = TestBed.createComponent(GridHelperComponent);
22 | component = fixture.componentInstance;
23 | return TestBed.compileComponents();
24 | }));
25 |
26 | it('init', () => {
27 | fixture.detectChanges();
28 | expect(component).toBeTruthy();
29 | });
30 |
31 | });
32 | });
33 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/object/helper/grid-helper.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, Input, Optional, SkipSelf } from '@angular/core';
2 | import * as THREE from 'three';
3 | import { RendererService } from '../../renderer/renderer.service';
4 | import { provideParent } from '../../util';
5 | import { AbstractObject3D } from '../abstract-object-3d';
6 |
7 | @Component({
8 | selector: 'atft-grid-helper',
9 | providers: [provideParent(GridHelperComponent)],
10 | template: ''
11 | })
12 | export class GridHelperComponent extends AbstractObject3D {
13 |
14 | @Input() size!: number;
15 | @Input() divisions!: number;
16 |
17 | constructor(
18 | protected override rendererService: RendererService,
19 | @SkipSelf() @Optional() protected override parent: AbstractObject3D
20 | ) {
21 | super(rendererService, parent);
22 | }
23 |
24 | protected newObject3DInstance(): THREE.GridHelper {
25 | // console.log('GridHelperComponent.newObject3DInstance');
26 | return new THREE.GridHelper(this.size, this.divisions);
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/object/helper/index.ts:
--------------------------------------------------------------------------------
1 | export * from './atft-helper.module';
2 | export * from './grid-helper.component';
3 | export * from './axes-helper.component';
4 | export * from './empty.component';
5 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/object/index.ts:
--------------------------------------------------------------------------------
1 | export * from './atft-object.module';
2 |
3 | export * from './abstract-object-3d';
4 | export * from './abstract-empty-directive';
5 | export * from './abstract-lazy-object-3d';
6 | export * from './content-projection.component';
7 | export * from './scene.component';
8 |
9 | export * from './helper';
10 | export * from './light';
11 | export * from './loader';
12 | export * from './mesh';
13 | export * from './connector';
14 | export * from './text';
15 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/object/light/ambient-light.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, Input, Optional, SkipSelf } from '@angular/core';
2 | import * as THREE from 'three';
3 | import { RendererService } from '../../renderer/renderer.service';
4 | import { provideParent } from '../../util';
5 | import { AbstractObject3D } from '../abstract-object-3d';
6 |
7 | @Component({
8 | selector: 'atft-ambient-light',
9 | providers: [provideParent(AmbientLightComponent)],
10 | template: ''
11 | })
12 | export class AmbientLightComponent extends AbstractObject3D {
13 |
14 | @Input() color: string | number = '#FFFFFF';
15 | @Input() intensity = 0.8;
16 |
17 | constructor(
18 | protected override rendererService: RendererService,
19 | @SkipSelf() @Optional() protected override parent: AbstractObject3D
20 | ) {
21 | super(rendererService, parent);
22 | }
23 |
24 | protected newObject3DInstance() {
25 | const light = new THREE.AmbientLight(this.color);
26 | light.intensity = this.intensity;
27 | return light;
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/object/light/atft-light.module.ts:
--------------------------------------------------------------------------------
1 | import {NgModule} from '@angular/core';
2 | import {CommonModule} from '@angular/common';
3 | import {PointLightComponent} from './point-light.component';
4 | import {HemisphereLightComponent} from './hemisphere-light.component';
5 | import {DirectionalLightComponent} from './directional-light.component';
6 | import {AmbientLightComponent} from './ambient-light.component';
7 |
8 |
9 | @NgModule({
10 | declarations: [
11 | DirectionalLightComponent,
12 | HemisphereLightComponent,
13 | PointLightComponent,
14 | AmbientLightComponent
15 | ],
16 | imports: [
17 | CommonModule
18 | ],
19 | exports: [
20 | DirectionalLightComponent,
21 | HemisphereLightComponent,
22 | PointLightComponent,
23 | AmbientLightComponent
24 | ]
25 | })
26 | export class AtftLightModule {
27 | }
28 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/object/light/directional-light.component.spec.ts:
--------------------------------------------------------------------------------
1 | import {RendererService} from '../../renderer/renderer.service';
2 | import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';
3 | import {DirectionalLightComponent} from './directional-light.component';
4 | import {StatsService} from '../../stats';
5 |
6 | describe('light', () => {
7 | describe('DirectionalLightComponent', () => {
8 | let component: DirectionalLightComponent;
9 | let fixture: ComponentFixture;
10 |
11 | beforeEach(waitForAsync(() => {
12 | TestBed.configureTestingModule({
13 | declarations: [
14 | DirectionalLightComponent
15 | ],
16 | providers: [
17 | StatsService,
18 | RendererService
19 | ]
20 | });
21 | fixture = TestBed.createComponent(DirectionalLightComponent);
22 | component = fixture.componentInstance;
23 | component.castShadow = true;
24 | return TestBed.compileComponents();
25 | }));
26 |
27 | it('init', () => {
28 | fixture.detectChanges();
29 | expect(component).toBeTruthy();
30 | });
31 |
32 | });
33 | });
34 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/object/light/directional-light.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, Input, Optional, SkipSelf } from '@angular/core';
2 | import * as THREE from 'three';
3 | import { RendererService } from '../../renderer/renderer.service';
4 | import { provideParent } from '../../util';
5 | import { AbstractObject3D } from '../abstract-object-3d';
6 |
7 | @Component({
8 | selector: 'atft-directional-light',
9 | providers: [provideParent(DirectionalLightComponent)],
10 | template: ''
11 | })
12 | export class DirectionalLightComponent extends AbstractObject3D {
13 |
14 | @Input() color: string | number = '#FFFFFF';
15 | @Input() intensity = 1;
16 | // by default, target is 0,0,0
17 | @Input() target = new THREE.Object3D();
18 | @Input() castShadow = true;
19 |
20 | constructor(
21 | protected override rendererService: RendererService,
22 | @SkipSelf() @Optional() protected override parent: AbstractObject3D
23 | ) {
24 | super(rendererService, parent);
25 | }
26 |
27 | protected newObject3DInstance() {
28 |
29 | const light = new THREE.DirectionalLight(
30 | this.color,
31 | this.intensity
32 | );
33 |
34 | light.target = this.target;
35 |
36 | if (this.castShadow === true) {
37 | light.castShadow = this.castShadow;
38 | // TODO: props
39 | light.shadow.camera.top = 100;
40 | light.shadow.camera.bottom = -100;
41 | light.shadow.camera.left = -100;
42 | light.shadow.camera.right = 100;
43 | light.shadow.camera.near = 0.1;
44 | light.shadow.camera.far = 500;
45 | light.shadow.mapSize.set(1024, 1024);
46 | light.shadow.bias = -0.001;
47 |
48 | }
49 | return light;
50 |
51 | }
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/object/light/hemisphere-light.component.spec.ts:
--------------------------------------------------------------------------------
1 | import {RendererService} from '../../renderer/renderer.service';
2 | import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';
3 | import {HemisphereLightComponent} from './hemisphere-light.component';
4 | import {StatsService} from '../../stats';
5 |
6 | describe('light', () => {
7 | describe('HemisphereLightComponent', () => {
8 | let component: HemisphereLightComponent;
9 | let fixture: ComponentFixture;
10 |
11 | beforeEach(waitForAsync(() => {
12 | TestBed.configureTestingModule({
13 | declarations: [
14 | HemisphereLightComponent
15 | ],
16 | providers: [
17 | StatsService,
18 | RendererService
19 | ]
20 | });
21 | fixture = TestBed.createComponent(HemisphereLightComponent);
22 | component = fixture.componentInstance;
23 | return TestBed.compileComponents();
24 | }));
25 |
26 | it('init', () => {
27 | fixture.detectChanges();
28 | expect(component).toBeTruthy();
29 | });
30 |
31 | });
32 | });
33 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/object/light/hemisphere-light.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, Input, Optional, SkipSelf } from '@angular/core';
2 | import * as THREE from 'three';
3 | import { RendererService } from '../../renderer/renderer.service';
4 | import { provideParent } from '../../util';
5 | import { AbstractObject3D } from '../abstract-object-3d';
6 |
7 | @Component({
8 | selector: 'atft-hemisphere-light',
9 | providers: [provideParent(HemisphereLightComponent)],
10 | template: ''
11 | })
12 | export class HemisphereLightComponent extends AbstractObject3D {
13 |
14 | @Input() skyColor: string | number = '#ffffff';
15 | @Input() groundColor: string | number = '#444444';
16 | @Input() intensity = 1;
17 |
18 | constructor(
19 | protected override rendererService: RendererService,
20 | @SkipSelf() @Optional() protected override parent: AbstractObject3D
21 | ) {
22 | super(rendererService, parent);
23 | }
24 |
25 | protected newObject3DInstance() {
26 |
27 | const light = new THREE.HemisphereLight(
28 | this.skyColor,
29 | this.groundColor,
30 | this.intensity
31 | );
32 |
33 | return light;
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/object/light/index.ts:
--------------------------------------------------------------------------------
1 | export * from './atft-light.module';
2 | export * from './point-light.component';
3 | export * from './directional-light.component';
4 | export * from './hemisphere-light.component';
5 | export * from './ambient-light.component';
6 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/object/light/point-light.component.spec.ts:
--------------------------------------------------------------------------------
1 | import {RendererService} from '../../renderer/renderer.service';
2 | import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';
3 | import {PointLightComponent} from './point-light.component';
4 | import {StatsService} from '../../stats';
5 |
6 | describe('light', () => {
7 | describe('PointLightComponent', () => {
8 | let component: PointLightComponent;
9 | let fixture: ComponentFixture;
10 |
11 | beforeEach(waitForAsync(() => {
12 | TestBed.configureTestingModule({
13 | declarations: [
14 | PointLightComponent
15 | ],
16 | providers: [
17 | StatsService,
18 | RendererService
19 | ]
20 | });
21 | fixture = TestBed.createComponent(PointLightComponent);
22 | component = fixture.componentInstance;
23 | component.castShadow = true;
24 | return TestBed.compileComponents();
25 | }));
26 |
27 | it('init', () => {
28 | fixture.detectChanges();
29 | expect(component).toBeTruthy();
30 | });
31 |
32 | });
33 | });
34 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/object/loader/abstract-model-loader.ts:
--------------------------------------------------------------------------------
1 | import {Directive, Input} from '@angular/core';
2 | import {AbstractLazyObject3D} from '../abstract-lazy-object-3d';
3 |
4 | /**
5 | * Helper parentScene class for model loader.
6 | *
7 | * @see ObjLoaderComponent
8 | */
9 | @Directive()
10 | export abstract class AbstractModelLoader extends AbstractLazyObject3D {
11 |
12 | protected _model!: string;
13 |
14 | /**
15 | * The model data source (usually a URI).
16 | * Settings this property only hides the previous model upon successful
17 | * loading of the new one. This especially means that if the new data source
18 | * is invalid, the old model will *not* be removed from the scene.
19 | */
20 | @Input()
21 | public set model(newModelUrl: string) {
22 | // console.log('AbstractModelLoader.model', newModelUrl);
23 | this._model = newModelUrl;
24 | if (this.object) {
25 | super.startLoading();
26 | }
27 | }
28 |
29 | /**
30 | * The current model data source (usually a URI).
31 | */
32 | public get model() {
33 | return this._model;
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/object/loader/atft-loader.module.ts:
--------------------------------------------------------------------------------
1 | import {NgModule} from '@angular/core';
2 | import {CommonModule} from '@angular/common';
3 | import {ObjectLoaderComponent} from './object-loader.component';
4 | import {ObjLoaderComponent} from './obj-loader.component';
5 | import {SVGLoaderComponent} from './svg-loader.component';
6 | import {StlLoaderComponent} from './stl-loader.component';
7 | import {AudioLoaderComponent} from "./audio-loader.component";
8 |
9 |
10 | @NgModule({
11 | declarations: [
12 | ObjLoaderComponent,
13 | ObjectLoaderComponent,
14 | SVGLoaderComponent,
15 | StlLoaderComponent,
16 | AudioLoaderComponent
17 | ],
18 | imports: [
19 | CommonModule
20 | ],
21 | exports: [
22 | ObjLoaderComponent,
23 | ObjectLoaderComponent,
24 | SVGLoaderComponent,
25 | StlLoaderComponent,
26 | AudioLoaderComponent
27 | ]
28 | })
29 | export class AtftLoaderModule {
30 | }
31 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/object/loader/index.ts:
--------------------------------------------------------------------------------
1 | export * from './atft-loader.module';
2 | export * from './obj-loader.component';
3 | export * from './object-loader.component';
4 | export * from './svg-loader.component';
5 | export * from './stl-loader.component';
6 | export * from './abstract-model-loader';
7 | export * from './audio-loader.component';
8 | export * from './services';
9 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/object/loader/obj-loader.component.spec.ts:
--------------------------------------------------------------------------------
1 | import {RendererService} from '../../renderer/renderer.service';
2 | import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';
3 | import {ObjLoaderComponent} from './obj-loader.component';
4 | import {AtftLoaderModule} from './atft-loader.module';
5 | import {StatsService} from '../../stats';
6 | import {ObjLoaderService} from './services';
7 |
8 | describe('loader', () => {
9 | describe('ObjLoaderComponent', () => {
10 | let component: ObjLoaderComponent;
11 | let fixture: ComponentFixture;
12 |
13 | beforeEach(waitForAsync(() => {
14 | TestBed.configureTestingModule({
15 | imports: [
16 | AtftLoaderModule
17 | ],
18 | providers: [
19 | StatsService,
20 | RendererService,
21 | ObjLoaderService
22 | ]
23 | });
24 | fixture = TestBed.createComponent(ObjLoaderComponent);
25 | return TestBed.compileComponents();
26 | }));
27 |
28 | it('load', waitForAsync(() => {
29 | component = fixture.componentInstance;
30 | component.model = '/assets/model/smiley/smiley.obj';
31 | component.material = '/assets/model/smiley/smiley.mtl';
32 | fixture.detectChanges();
33 | expect(component).toBeTruthy();
34 | fixture.detectChanges();
35 | }));
36 |
37 | });
38 | });
39 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/object/loader/object-loader.component.spec.ts:
--------------------------------------------------------------------------------
1 | import {RendererService} from '../../renderer/renderer.service';
2 | import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';
3 | import {AtftLoaderModule} from './atft-loader.module';
4 | import {ObjectLoaderComponent} from './object-loader.component';
5 | import {StatsService} from '../../stats';
6 |
7 | describe('loader', () => {
8 | describe('ObjectLoaderComponent', () => {
9 | let component: ObjectLoaderComponent;
10 | let fixture: ComponentFixture;
11 |
12 | beforeEach(waitForAsync(() => {
13 | TestBed.configureTestingModule({
14 | imports: [
15 | AtftLoaderModule
16 | ],
17 | providers: [
18 | StatsService,
19 | RendererService
20 | ]
21 | });
22 | fixture = TestBed.createComponent(ObjectLoaderComponent);
23 | return TestBed.compileComponents();
24 | }));
25 |
26 | it('load', waitForAsync(() => {
27 | component = fixture.componentInstance;
28 | component.model = '/assets/model/Server.json';
29 | fixture.detectChanges();
30 |
31 | expect(component).toBeTruthy();
32 | fixture.detectChanges();
33 | }));
34 |
35 | });
36 | });
37 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/object/loader/object-loader.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, Optional, SkipSelf } from '@angular/core';
2 |
3 | import * as THREE from 'three';
4 | import { RendererService } from '../../renderer/renderer.service';
5 | import { provideParent } from '../../util';
6 | import { AbstractObject3D } from '../abstract-object-3d';
7 | import { AbstractModelLoader } from './abstract-model-loader';
8 |
9 | @Component({
10 | selector: 'atft-object-loader',
11 | providers: [provideParent(ObjectLoaderComponent)],
12 | template: ''
13 | })
14 | export class ObjectLoaderComponent extends AbstractModelLoader {
15 | private loader = new THREE.ObjectLoader();
16 |
17 | constructor(
18 | protected override rendererService: RendererService,
19 | @SkipSelf() @Optional() protected override parent: AbstractObject3D
20 | ) {
21 | super(rendererService, parent);
22 | }
23 |
24 | protected async loadLazyObject() {
25 | // console.log('ObjectLoaderComponent.loadLazyObject');
26 | return new Promise((resolve, reject) => {
27 | this.loader.load(this.model, model => {
28 | // BUG #95: it seems that some textures loaded after last render (and model has black texture instead)
29 | resolve(model);
30 | },
31 | undefined,
32 | reject
33 | );
34 | });
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/object/loader/services/abstract-asset.service.ts:
--------------------------------------------------------------------------------
1 | import {Injectable} from '@angular/core';
2 |
3 | const SEPARATOR = ':';
4 | const REPLACE_SYMBOL = '?';
5 |
6 | export interface BaseAssetSource {
7 | url: string;
8 | }
9 |
10 | @Injectable()
11 | export abstract class AbstractAssetService {
12 |
13 | protected providers: Map = new Map();
14 | abstract defaultProvider: string;
15 |
16 | constructor() {
17 | this.init();
18 | }
19 |
20 | protected abstract init(): void;
21 |
22 | public registerProvider(key: string, source: T) {
23 | this.providers.set(key, source);
24 | }
25 |
26 | public setDefaultProvider(key: string) {
27 | this.defaultProvider = key;
28 | }
29 |
30 | public getSource(icon: string): T {
31 | if (icon) {
32 | if (icon.indexOf(SEPARATOR) > 0) {
33 | const args = icon.split(SEPARATOR);
34 | return this.getSourceByNamespace(args[0], args[1]);
35 | } else {
36 | return this.getSourceByNamespace(this.defaultProvider, icon);
37 | }
38 | } else {
39 | return this.defaultIfNotFound(icon);
40 | }
41 | }
42 |
43 | public getSourceByNamespace(namespace: string, icon: string): T {
44 | // console.log('AbstractAssetService.getUrlByNamespace', namespace + ', ' + icon);
45 | const provider = this.providers.get(namespace);
46 | if (!provider) {
47 | console.warn('Icon provider not found', provider);
48 | return this.defaultIfNotFound(icon);
49 | }
50 | const finalUrl = provider.url.replace(REPLACE_SYMBOL, icon);
51 | // console.log('AbstractAssetService.getUrlByNamespace url', svgUrl);
52 | return this.getFinalResult(finalUrl, provider);
53 | }
54 |
55 | public abstract getFinalResult(finalUrl: string, provider: T): T;
56 |
57 | public abstract defaultIfNotFound(icon: string): T;
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/object/loader/services/abstract-cache.service.ts:
--------------------------------------------------------------------------------
1 | export abstract class AbstractCacheService {
2 |
3 | protected cache = new Map>();
4 |
5 | public async load(key: string): Promise {
6 | const cacheHit = this.cache.get(key);
7 | if (await cacheHit) {
8 | // console.log('AbstractCacheService.cacheHit', key);
9 | if (cacheHit) {
10 | return cacheHit;
11 | } else {
12 | return Promise.reject("Failed to get value from cache")
13 | }
14 | } else {
15 | // console.log('AbstractCacheService.cacheMiss', key);
16 | const cacheMiss = this.getValue(key);
17 | this.cache.set(key, cacheMiss);
18 | return cacheMiss;
19 | }
20 | }
21 |
22 | protected abstract getValue(key: string): Promise;
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/object/loader/services/font.service.ts:
--------------------------------------------------------------------------------
1 | import {Injectable} from '@angular/core';
2 | import {AbstractCacheService} from './abstract-cache.service';
3 | import {Font, FontLoader} from 'three/examples/jsm/loaders/FontLoader.js';
4 |
5 | @Injectable()
6 | export class FontService extends AbstractCacheService {
7 |
8 | protected getValue(key: string): Promise {
9 | // console.log('FontService.getValue');
10 | return new Promise(resolve => {
11 | const loader = new FontLoader();
12 | loader.load(key, resolve);
13 | });
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/object/loader/services/index.ts:
--------------------------------------------------------------------------------
1 | export * from './font.service';
2 | export * from './svg-loader.service';
3 | export * from './icon.service';
4 | export * from './obj-loader.service';
5 | export * from './model.service';
6 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/object/loader/services/model.service.ts:
--------------------------------------------------------------------------------
1 | import {Injectable} from '@angular/core';
2 | import {AbstractAssetService, BaseAssetSource} from './abstract-asset.service';
3 |
4 |
5 | @Injectable()
6 | export class ModelService extends AbstractAssetService {
7 |
8 | defaultProvider = '3d';
9 |
10 | protected init() {
11 | this.registerProvider('3d', {
12 | url: 'https://raw.githubusercontent.com/makimenko/files/master/actor-models/?.obj'
13 | });
14 | }
15 |
16 | defaultIfNotFound(icon: string): BaseAssetSource {
17 | return {
18 | url: icon
19 | };
20 | }
21 |
22 | // eslint-disable-next-line @typescript-eslint/no-unused-vars
23 | getFinalResult(finalUrl: string, provider: BaseAssetSource): BaseAssetSource {
24 | return {
25 | url: finalUrl
26 | };
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/object/loader/services/obj-loader.service.ts:
--------------------------------------------------------------------------------
1 | import {Injectable} from '@angular/core';
2 | import {AbstractCacheService} from './abstract-cache.service';
3 | import {OBJLoader} from 'three/examples/jsm/loaders/OBJLoader';
4 | import * as THREE from 'three';
5 | import {MTLLoader} from 'three/examples/jsm/loaders/MTLLoader';
6 |
7 |
8 | @Injectable()
9 | export class ObjLoaderService extends AbstractCacheService {
10 |
11 | private loader = new OBJLoader();
12 |
13 | protected getValue(key: string): Promise {
14 | // console.log('ObjLoaderService.getValue');
15 | return new Promise((resolve, reject) => {
16 | this.loader.load(key, model => {
17 | resolve(model);
18 | }, undefined, reject);
19 | });
20 | }
21 |
22 | public setMaterials(materialCreator: MTLLoader.MaterialCreator): void {
23 | this.loader.setMaterials(materialCreator);
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/object/loader/services/svg-loader.service.ts:
--------------------------------------------------------------------------------
1 | import {Injectable} from '@angular/core';
2 | import {ShapePath} from 'three';
3 | import {AbstractCacheService} from './abstract-cache.service';
4 | import {SVGLoader} from '../../../threejs-fork/SVGLoader';
5 |
6 |
7 | @Injectable()
8 | export class SvgLoaderService extends AbstractCacheService {
9 |
10 | protected getValue(key: string): Promise {
11 | // console.log('SvgLoaderService.getValue');
12 | return new Promise((resolve, reject) => {
13 | const loader = new SVGLoader();
14 | // @ts-ignore
15 | loader.load(key, data => {
16 | resolve(data.paths);
17 | },
18 | undefined,
19 | reject
20 | );
21 | });
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/object/loader/stl-loader.component.spec.ts:
--------------------------------------------------------------------------------
1 | import {RendererService} from '../../renderer/renderer.service';
2 | import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';
3 | import {IconService, SvgLoaderService} from './services';
4 | import {SVGLoaderComponent} from './svg-loader.component';
5 | import {StatsService} from '../../stats';
6 | import {StlLoaderComponent} from './stl-loader.component';
7 |
8 | describe('loader', () => {
9 |
10 | describe('StlLoaderComponent', () => {
11 | let component: StlLoaderComponent;
12 | let fixture: ComponentFixture;
13 |
14 | beforeEach(waitForAsync(() => {
15 | TestBed.configureTestingModule({
16 | declarations: [
17 | SVGLoaderComponent
18 | ],
19 | providers: [
20 | StatsService,
21 | RendererService,
22 | SvgLoaderService,
23 | IconService
24 | ]
25 | });
26 | fixture = TestBed.createComponent(StlLoaderComponent);
27 | return TestBed.compileComponents();
28 | }));
29 |
30 | it('load', (() => {
31 | component = fixture.componentInstance;
32 | component.model = '/assets/model/Menger_sponge_sample.stl';
33 | component.materialColor = '#ff0000';
34 | fixture.detectChanges();
35 | component.ngOnInit();
36 |
37 | expect(component).toBeTruthy();
38 | }));
39 |
40 | });
41 |
42 | });
43 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/object/loader/stl-loader.component.ts:
--------------------------------------------------------------------------------
1 | import {Component, Input, Optional, SkipSelf} from '@angular/core';
2 |
3 | import * as THREE from 'three';
4 | import {RendererService} from '../../renderer/renderer.service';
5 | import {appliedMaterial, provideParent} from '../../util';
6 | import {AbstractObject3D} from '../abstract-object-3d';
7 | import {AbstractModelLoader} from './abstract-model-loader';
8 | import {STLLoader} from 'three/examples/jsm/loaders/STLLoader';
9 |
10 | @Component({
11 | selector: 'atft-stl-loader',
12 | providers: [provideParent(StlLoaderComponent)],
13 | template: ''
14 | })
15 | export class StlLoaderComponent extends AbstractModelLoader {
16 | private loader = new STLLoader();
17 |
18 | @Input()
19 | material!: string;
20 |
21 | @Input()
22 | materialColor: string | number = '#FFFFFF';
23 |
24 | @Input()
25 | depthWrite = true;
26 |
27 | constructor(
28 | protected override rendererService: RendererService,
29 | @SkipSelf() @Optional() protected override parent: AbstractObject3D
30 | ) {
31 | super(rendererService, parent);
32 | }
33 |
34 | protected async loadLazyObject() {
35 | // console.log('StlLoaderComponent.loadLazyObject');
36 | return new Promise((resolve, reject) => {
37 | this.loader.load(this.model, geometry => {
38 | const material = appliedMaterial(this.materialColor, this.material, this.depthWrite);
39 | const mesh = new THREE.Mesh(geometry, material);
40 | resolve(mesh);
41 | },
42 | undefined,
43 | reject
44 | );
45 | });
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/object/mesh/abstract-mesh-3d.ts:
--------------------------------------------------------------------------------
1 | import { Input, OnChanges, SimpleChanges, Directive } from '@angular/core';
2 | import * as THREE from 'three';
3 | import {AbstractObject3D} from '../abstract-object-3d';
4 | import {appliedMaterial} from '../../util';
5 |
6 | @Directive()
7 | export abstract class AbstractMesh extends AbstractObject3D implements OnChanges {
8 |
9 | @Input()
10 | material!: string;
11 |
12 | @Input()
13 | materialColor: string | number = '#5DADE2';
14 |
15 | @Input()
16 | castShadow = true;
17 |
18 | @Input()
19 | receiveShadow = true;
20 |
21 | @Input()
22 | depthWrite = true;
23 |
24 | protected getMaterial(): THREE.Material {
25 | return appliedMaterial(this.materialColor, this.material, this.depthWrite);
26 | }
27 |
28 | protected applyShadowProps(mesh: THREE.Mesh) {
29 | mesh.castShadow = this.castShadow;
30 | mesh.receiveShadow = this.receiveShadow;
31 | }
32 |
33 | public override ngOnChanges(changes: SimpleChanges) {
34 | super.ngOnChanges(changes);
35 | if (!this.getObject()) {
36 | return;
37 | }
38 |
39 | let mustRerender = false;
40 | if (['material', 'materialColor', 'depthWrite'].some(propName => propName in changes)) {
41 | this.applyMaterial();
42 | mustRerender = true;
43 | }
44 |
45 | if (mustRerender) {
46 | this.rendererService.render();
47 | }
48 | }
49 |
50 | public applyMaterial() {
51 | this.getObject().material = this.getMaterial();
52 | }
53 |
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/object/mesh/atft-mesh.module.ts:
--------------------------------------------------------------------------------
1 | import {NgModule} from '@angular/core';
2 | import {CommonModule} from '@angular/common';
3 | import {BoxMeshComponent} from './box-mesh.component';
4 | import {CylinderMeshComponent} from './cylinder-mesh.component';
5 | import {FrameMeshComponent} from './frame-mesh.component';
6 | import {PlaneMeshComponent} from './plane-mesh.component';
7 | import {SphereMeshComponent} from './sphere-mesh.component';
8 | import {TorusMeshComponent} from './torus-mesh.component';
9 | import {GridMeshComponent} from './grid-mesh.component';
10 | import {VideoMeshComponent} from './video-mesh.component';
11 |
12 |
13 | @NgModule({
14 | declarations: [
15 | BoxMeshComponent,
16 | CylinderMeshComponent,
17 | FrameMeshComponent,
18 | PlaneMeshComponent,
19 | SphereMeshComponent,
20 | TorusMeshComponent,
21 | GridMeshComponent,
22 | VideoMeshComponent
23 | ],
24 | imports: [
25 | CommonModule
26 | ],
27 | exports: [
28 | BoxMeshComponent,
29 | CylinderMeshComponent,
30 | FrameMeshComponent,
31 | PlaneMeshComponent,
32 | SphereMeshComponent,
33 | TorusMeshComponent,
34 | GridMeshComponent,
35 | VideoMeshComponent
36 | ]
37 | })
38 | export class AtftMeshModule {
39 | }
40 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/object/mesh/box-mesh.component.spec.ts:
--------------------------------------------------------------------------------
1 | import {RendererService} from '../../renderer/renderer.service';
2 | import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';
3 | import {BoxMeshComponent} from './box-mesh.component';
4 | import {StatsService} from '../../stats';
5 |
6 | describe('mesh', () => {
7 | describe('BoxMeshComponent', () => {
8 | let component: BoxMeshComponent;
9 | let fixture: ComponentFixture;
10 |
11 | beforeEach(waitForAsync(() => {
12 | TestBed.configureTestingModule({
13 | declarations: [
14 | BoxMeshComponent
15 | ],
16 | providers: [
17 | StatsService,
18 | RendererService
19 | ]
20 | });
21 | fixture = TestBed.createComponent(BoxMeshComponent);
22 | component = fixture.componentInstance;
23 | return TestBed.compileComponents();
24 | }));
25 |
26 | it('should create an instance', () => {
27 | fixture.detectChanges();
28 | expect(component).toBeTruthy();
29 | });
30 |
31 | });
32 | });
33 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/object/mesh/cylinder-mesh.component.spec.ts:
--------------------------------------------------------------------------------
1 | import {CylinderMeshComponent} from './cylinder-mesh.component';
2 | import {RendererService} from '../../renderer/renderer.service';
3 | import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';
4 | import {StatsService} from '../../stats';
5 |
6 | describe('mesh', () => {
7 | describe('CylinderMeshComponent', () => {
8 | let component: CylinderMeshComponent;
9 | let fixture: ComponentFixture;
10 |
11 | beforeEach(waitForAsync(() => {
12 | TestBed.configureTestingModule({
13 | declarations: [
14 | CylinderMeshComponent
15 | ],
16 | providers: [
17 | StatsService,
18 | RendererService
19 | ]
20 | });
21 | fixture = TestBed.createComponent(CylinderMeshComponent);
22 | component = fixture.componentInstance;
23 | return TestBed.compileComponents();
24 | }));
25 |
26 | it('should create an instance', () => {
27 | fixture.detectChanges();
28 | expect(component).toBeTruthy();
29 | });
30 |
31 | });
32 | });
33 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/object/mesh/cylinder-mesh.component.ts:
--------------------------------------------------------------------------------
1 | import {Component, Input, Optional, SkipSelf} from '@angular/core';
2 | import * as THREE from 'three';
3 | import {provideParent} from '../../util';
4 | import {AbstractMesh} from './abstract-mesh-3d';
5 | import {AbstractObject3D} from '../abstract-object-3d';
6 | import {RendererService} from '../../renderer/renderer.service';
7 |
8 | @Component({
9 | selector: 'atft-cylinder-mesh',
10 | providers: [provideParent(CylinderMeshComponent)],
11 | template: ''
12 | })
13 | export class CylinderMeshComponent extends AbstractMesh {
14 |
15 | @Input()
16 | radiusTop = 1.0;
17 | @Input()
18 | radiusBottom = 1.0;
19 | @Input()
20 | height = 1.0;
21 | @Input()
22 | radialSegments = 8;
23 | @Input()
24 | heightSegments = 1;
25 | @Input()
26 | openEnded = false;
27 | @Input()
28 | thetaStart = 0.0;
29 | @Input()
30 | thetaLength = 2 * Math.PI;
31 |
32 | constructor(
33 | protected override rendererService: RendererService,
34 | @SkipSelf() @Optional() protected override parent: AbstractObject3D
35 | ) {
36 | super(rendererService, parent);
37 | // console.log('CylinderMeshComponent.constructor', parentScene);
38 | }
39 |
40 | protected newObject3DInstance(): THREE.Mesh {
41 | // console.log('CylinderMeshComponent.newObject3DInstance');
42 | const geometry = new THREE.CylinderGeometry(this.radiusTop, this.radiusBottom, this.height, this.radialSegments, this.heightSegments,
43 | this.openEnded, this.thetaStart, this.thetaLength);
44 | const material = this.getMaterial();
45 | const mesh = new THREE.Mesh(geometry, material);
46 | this.applyShadowProps(mesh);
47 | return mesh;
48 | }
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/object/mesh/frame-mesh.component.spec.ts:
--------------------------------------------------------------------------------
1 | import {RendererService} from '../../renderer/renderer.service';
2 | import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';
3 | import {FrameMeshComponent} from './frame-mesh.component';
4 | import {StatsService} from '../../stats';
5 |
6 | describe('mesh', () => {
7 | describe('FrameMeshComponent', () => {
8 | let component: FrameMeshComponent;
9 | let fixture: ComponentFixture;
10 |
11 | beforeEach(waitForAsync(() => {
12 | TestBed.configureTestingModule({
13 | declarations: [
14 | FrameMeshComponent
15 | ],
16 | providers: [
17 | StatsService,
18 | RendererService
19 | ]
20 | });
21 | fixture = TestBed.createComponent(FrameMeshComponent);
22 | component = fixture.componentInstance;
23 | return TestBed.compileComponents();
24 | }));
25 |
26 | it('should create an instance', () => {
27 | fixture.detectChanges();
28 | expect(component).toBeTruthy();
29 | });
30 |
31 | });
32 | });
33 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/object/mesh/grid-mesh.component.spec.ts:
--------------------------------------------------------------------------------
1 | import {RendererService} from '../../renderer/renderer.service';
2 | import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';
3 | import {StatsService} from '../../stats/stats.service';
4 | import {GridMeshComponent} from './grid-mesh.component';
5 |
6 | describe('mesh', () => {
7 | describe('GridMeshComponent', () => {
8 | let component: GridMeshComponent;
9 | let fixture: ComponentFixture;
10 |
11 | beforeEach(waitForAsync(() => {
12 | TestBed.configureTestingModule({
13 | declarations: [
14 | GridMeshComponent
15 | ],
16 | providers: [
17 | StatsService,
18 | RendererService
19 | ]
20 | });
21 | fixture = TestBed.createComponent(GridMeshComponent);
22 | component = fixture.componentInstance;
23 | return TestBed.compileComponents();
24 | }));
25 |
26 | it('should create an instance', () => {
27 | fixture.detectChanges();
28 | expect(component).toBeTruthy();
29 | });
30 |
31 | });
32 | });
33 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/object/mesh/index.ts:
--------------------------------------------------------------------------------
1 | export * from './atft-mesh.module';
2 | export * from './abstract-mesh-3d';
3 | export * from './cylinder-mesh.component';
4 | export * from './sphere-mesh.component';
5 | export * from './torus-mesh.component';
6 | export * from './box-mesh.component';
7 | export * from './plane-mesh.component';
8 | export * from './frame-mesh.component';
9 | export * from './grid-mesh.component';
10 | export * from './video-mesh.component';
11 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/object/mesh/plane-mesh.component.spec.ts:
--------------------------------------------------------------------------------
1 | import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';
2 | import {RendererService} from '../../renderer/renderer.service';
3 | import {PlaneMeshComponent} from './plane-mesh.component';
4 | import {StatsService} from '../../stats';
5 |
6 | describe('mesh', () => {
7 | describe('PlaneMeshComponent', () => {
8 | let component: PlaneMeshComponent;
9 | let fixture: ComponentFixture;
10 |
11 | beforeEach(waitForAsync(() => {
12 | TestBed.configureTestingModule({
13 | declarations: [
14 | PlaneMeshComponent
15 | ],
16 | providers: [
17 | StatsService,
18 | RendererService
19 | ]
20 | });
21 | fixture = TestBed.createComponent(PlaneMeshComponent);
22 | component = fixture.componentInstance;
23 | return TestBed.compileComponents();
24 | }));
25 |
26 | it('should create an instance', () => {
27 | fixture.detectChanges();
28 | expect(component).toBeTruthy();
29 | });
30 |
31 | });
32 | });
33 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/object/mesh/sphere-mesh.component.spec.ts:
--------------------------------------------------------------------------------
1 | import {SphereMeshComponent} from './sphere-mesh.component';
2 | import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';
3 | import {RendererService} from '../../renderer/renderer.service';
4 | import {StatsService} from '../../stats';
5 |
6 | describe('mesh', () => {
7 | describe('SphereMeshComponent', () => {
8 | let component: SphereMeshComponent;
9 | let fixture: ComponentFixture;
10 |
11 | beforeEach(waitForAsync(() => {
12 | TestBed.configureTestingModule({
13 | declarations: [
14 | SphereMeshComponent
15 | ],
16 | providers: [
17 | StatsService,
18 | RendererService
19 | ]
20 | });
21 | fixture = TestBed.createComponent(SphereMeshComponent);
22 | component = fixture.componentInstance;
23 | return TestBed.compileComponents();
24 | }));
25 |
26 | it('should create an instance', () => {
27 | fixture.detectChanges();
28 | expect(component).toBeTruthy();
29 | });
30 |
31 | });
32 | });
33 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/object/mesh/sphere-mesh.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, Input, Optional, SkipSelf } from '@angular/core';
2 | import * as THREE from 'three';
3 | import { RendererService } from '../../renderer/renderer.service';
4 | import { provideParent } from '../../util';
5 | import { AbstractObject3D } from '../abstract-object-3d';
6 | import { AbstractMesh } from './abstract-mesh-3d';
7 |
8 | @Component({
9 | selector: 'atft-sphere-mesh',
10 | providers: [provideParent(SphereMeshComponent)],
11 | template: ''
12 | })
13 | export class SphereMeshComponent extends AbstractMesh {
14 |
15 | @Input() radius!: number;
16 | @Input() widthSegments!: number;
17 | @Input() heightSegments!: number;
18 |
19 | constructor(
20 | protected override rendererService: RendererService,
21 | @SkipSelf() @Optional() protected override parent: AbstractObject3D
22 | ) {
23 | super(rendererService, parent);
24 | }
25 |
26 | protected newObject3DInstance(): THREE.Mesh {
27 | // console.log('SphereMeshComponent.newObject3DInstance');
28 | const geometry = new THREE.SphereGeometry(this.radius, this.widthSegments, this.heightSegments);
29 | const material = this.getMaterial();
30 | const mesh = new THREE.Mesh(geometry, material);
31 | this.applyShadowProps(mesh);
32 | return mesh;
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/object/mesh/torus-mesh.component.spec.ts:
--------------------------------------------------------------------------------
1 | import {TorusMeshComponent} from './torus-mesh.component';
2 | import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';
3 | import {RendererService} from '../../renderer/renderer.service';
4 | import {StatsService} from '../../stats';
5 |
6 | describe('mesh', () => {
7 | describe('TorusMeshComponent', () => {
8 | let component: TorusMeshComponent;
9 | let fixture: ComponentFixture;
10 |
11 | beforeEach(waitForAsync(() => {
12 | TestBed.configureTestingModule({
13 | declarations: [
14 | TorusMeshComponent
15 | ],
16 | providers: [
17 | StatsService,
18 | RendererService
19 | ]
20 | });
21 | fixture = TestBed.createComponent(TorusMeshComponent);
22 | component = fixture.componentInstance;
23 | return TestBed.compileComponents();
24 | }));
25 |
26 |
27 | it('should create an instance', () => {
28 | fixture.detectChanges();
29 | expect(component).toBeTruthy();
30 | });
31 |
32 | });
33 | });
34 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/object/mesh/torus-mesh.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, Input, Optional, SkipSelf } from '@angular/core';
2 | import * as THREE from 'three';
3 | import { RendererService } from '../../renderer/renderer.service';
4 | import { provideParent } from '../../util';
5 | import { AbstractObject3D } from '../abstract-object-3d';
6 | import { AbstractMesh } from './abstract-mesh-3d';
7 |
8 | @Component({
9 | selector: 'atft-torus-mesh',
10 | providers: [provideParent(TorusMeshComponent)],
11 | template: ''
12 | })
13 | export class TorusMeshComponent extends AbstractMesh {
14 |
15 | /**
16 | * Radius of the torus, from the center of the torus to the center of the tube
17 | */
18 | @Input() radius = 0.4;
19 |
20 | /**
21 | * Radius of the tube.
22 | */
23 | @Input()
24 | tube!: number;
25 |
26 | @Input()
27 | radialSegments = 8;
28 |
29 | @Input()
30 | tubularSegments = 6;
31 |
32 | @Input()
33 | arc: number = Math.PI * 2;
34 |
35 | constructor(
36 | protected override rendererService: RendererService,
37 | @SkipSelf() @Optional() protected override parent: AbstractObject3D
38 | ) {
39 | super(rendererService, parent);
40 | }
41 |
42 | protected newObject3DInstance(): THREE.Mesh {
43 | this.radius *= 1;
44 | this.tube *= 1;
45 | this.radialSegments *= 1;
46 | this.tubularSegments *= 1;
47 |
48 | const geometry = new THREE.TorusGeometry(this.radius, this.tube,
49 | this.radialSegments, this.tubularSegments);
50 | const material = this.getMaterial();
51 | const mesh = new THREE.Mesh(geometry, material);
52 | this.applyShadowProps(mesh);
53 | return mesh;
54 | }
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/object/scene.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, Input, OnChanges, Optional, SimpleChanges, SkipSelf } from '@angular/core';
2 | import * as THREE from 'three';
3 | import { RendererService } from '../renderer/renderer.service';
4 | import { provideParent } from '../util';
5 | import { AbstractObject3D } from './abstract-object-3d';
6 |
7 | @Component({
8 | selector: 'atft-scene',
9 | providers: [provideParent(SceneComponent)],
10 | template: ''
11 | })
12 | export class SceneComponent extends AbstractObject3D implements OnChanges {
13 |
14 | @Input() background: number | string = '#ffffff';
15 |
16 | constructor(
17 | protected override rendererService: RendererService,
18 | @SkipSelf() @Optional() protected override parent: AbstractObject3D
19 | ) {
20 | super(rendererService, parent);
21 | // TODO: directive?
22 | rendererService.setScene(this);
23 | }
24 |
25 | protected newObject3DInstance(): THREE.Scene {
26 | const scene = new THREE.Scene();
27 | scene.background = new THREE.Color(this.background);
28 | return scene;
29 | }
30 |
31 | public override updateParent() {
32 | // No Parent for scene. Skip: super.updateParent();
33 | }
34 |
35 | public override ngOnChanges(changes: SimpleChanges) {
36 | super.ngOnChanges(changes);
37 | if (!this.object) {
38 | return;
39 | }
40 |
41 | let modified = false;
42 |
43 | if (['background'].some(propName => propName in changes)) {
44 | this.getObject().background = new THREE.Color(this.background);
45 | modified = true;
46 | }
47 |
48 | if (modified) {
49 | this.rendererService.render();
50 | }
51 |
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/object/text/atft-text.module.ts:
--------------------------------------------------------------------------------
1 | import {NgModule} from '@angular/core';
2 | import {CommonModule} from '@angular/common';
3 | import {TextMeshComponent} from './text-mesh.component';
4 |
5 |
6 | @NgModule({
7 | declarations: [
8 | TextMeshComponent
9 | ],
10 | imports: [
11 | CommonModule
12 | ],
13 | exports: [
14 | TextMeshComponent
15 | ]
16 | })
17 | export class AtftTextModule {
18 | }
19 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/object/text/index.ts:
--------------------------------------------------------------------------------
1 | export * from './atft-text.module';
2 | export * from './text-mesh.component';
3 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/object/text/text-mesh.component.spec.ts:
--------------------------------------------------------------------------------
1 | import {RendererService} from '../../renderer/renderer.service';
2 | import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';
3 | import { FontService } from '../loader/services';
4 | import {TextMeshComponent} from './text-mesh.component';
5 | import {StatsService} from '../../stats';
6 |
7 | describe('text', () => {
8 | describe('TextMeshComponent', () => {
9 | let component: TextMeshComponent;
10 | let fixture: ComponentFixture;
11 |
12 | beforeEach(waitForAsync(() => {
13 | TestBed.configureTestingModule({
14 | declarations: [
15 | TextMeshComponent
16 | ],
17 | providers: [
18 | StatsService,
19 | RendererService,
20 | FontService
21 | ]
22 | });
23 | fixture = TestBed.createComponent(TextMeshComponent);
24 | component = fixture.componentInstance;
25 | component.castShadow = true;
26 | return TestBed.compileComponents();
27 | }));
28 |
29 | it('init', () => {
30 | fixture.detectChanges();
31 | expect(component).toBeTruthy();
32 | });
33 |
34 | it('text', () => {
35 | fixture.detectChanges();
36 | expect(component.text).toBe('Text');
37 | });
38 |
39 | it('color', () => {
40 | fixture.detectChanges();
41 | expect(component.materialColor).toBe('#DADADA');
42 | });
43 |
44 | });
45 | });
46 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/pipe/atft-pipe.module.ts:
--------------------------------------------------------------------------------
1 | import {NgModule} from '@angular/core';
2 | import {Deg2RadPipe} from './deg2rad.pipe';
3 | import {Rad2DegPipe} from './rad2deg.pipe';
4 | import {CommonModule} from '@angular/common';
5 |
6 |
7 | @NgModule({
8 | imports: [
9 | CommonModule
10 | ],
11 | declarations: [
12 | Deg2RadPipe,
13 | Rad2DegPipe
14 | ],
15 | exports: [
16 | Deg2RadPipe,
17 | Rad2DegPipe
18 | ]
19 | })
20 | export class AtftPipeModule {
21 | }
22 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/pipe/deg2rad.pipe.ts:
--------------------------------------------------------------------------------
1 | import {Pipe, PipeTransform} from '@angular/core';
2 |
3 | @Pipe({
4 | name: 'deg2rad'
5 | })
6 | export class Deg2RadPipe implements PipeTransform {
7 |
8 | /**
9 | * Converts degrees to radians
10 | * @param degree Degrees
11 | */
12 | transform(degrees: number): number {
13 | return (degrees / 180) * Math.PI;
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/pipe/index.ts:
--------------------------------------------------------------------------------
1 | export * from './atft-pipe.module';
2 |
3 | export * from './deg2rad.pipe';
4 | export * from './rad2deg.pipe';
5 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/pipe/rad2deg.pipe.ts:
--------------------------------------------------------------------------------
1 | import {Pipe, PipeTransform} from '@angular/core';
2 |
3 | @Pipe({
4 | name: 'rad2deg'
5 | })
6 | export class Rad2DegPipe implements PipeTransform {
7 |
8 | /**
9 | * Converts radians to degrees
10 | * @param radians Radians
11 | */
12 | transform(radians: number): number {
13 | return radians * (180 / Math.PI);
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/raycaster/atft-raycaster.module.ts:
--------------------------------------------------------------------------------
1 | import {NgModule} from '@angular/core';
2 | import {CommonModule} from '@angular/common';
3 | import {RaycasterGroupDirective} from './raycaster-group.directive';
4 | import {RaycasterService} from './raycaster.service';
5 | import {RaycasterCameraDirective} from './raycaster-camera.directive';
6 | import {RaycasterEnableDirective} from './raycaster-enable.directive';
7 |
8 |
9 | @NgModule({
10 | imports: [
11 | CommonModule
12 | ],
13 | declarations: [
14 | RaycasterGroupDirective,
15 | RaycasterCameraDirective,
16 | RaycasterEnableDirective
17 | ],
18 | providers: [
19 | RaycasterService
20 | ],
21 | exports: [
22 | RaycasterGroupDirective,
23 | RaycasterCameraDirective,
24 | RaycasterEnableDirective
25 | ]
26 | })
27 | export class AtftRaycasterModule {
28 | }
29 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/raycaster/index.ts:
--------------------------------------------------------------------------------
1 | export * from './atft-raycaster.module';
2 | export * from './raycaster-group.directive';
3 | export * from './raycaster-enable.directive';
4 | export * from './raycaster-camera.directive';
5 | export * from './raycaster.service';
6 | export * from './raycaster-event';
7 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/raycaster/raycaster-camera.directive.spec.ts:
--------------------------------------------------------------------------------
1 | import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';
2 | import {RendererService} from '../renderer/renderer.service';
3 | import {Component} from '@angular/core';
4 | import {AnimationService} from '../animation';
5 | import {RaycasterCameraDirective} from '../raycaster/raycaster-camera.directive';
6 | import {StatsService} from '../stats/stats.service';
7 | import {RaycasterService} from './raycaster.service';
8 | import {PerspectiveCameraComponent} from '../camera';
9 |
10 | @Component({
11 | selector: 'atft-mock',
12 | template: `
13 |
14 | `
15 | })
16 | class MockComponent {
17 |
18 | }
19 |
20 | describe('raycaster', () => {
21 |
22 | describe('RaycasterCameraDirective', () => {
23 | let component: MockComponent;
24 | let fixture: ComponentFixture;
25 |
26 | beforeEach(waitForAsync(() => {
27 | TestBed.configureTestingModule({
28 | declarations: [
29 | PerspectiveCameraComponent,
30 | MockComponent,
31 | RaycasterCameraDirective
32 | ],
33 | providers: [
34 | StatsService,
35 | RendererService,
36 | AnimationService,
37 | RaycasterService
38 | ]
39 | });
40 | fixture = TestBed.createComponent(MockComponent);
41 | component = fixture.componentInstance;
42 | return TestBed.compileComponents();
43 | }));
44 |
45 | it('init', () => {
46 | fixture.detectChanges();
47 | expect(component).toBeTruthy();
48 | });
49 | });
50 |
51 | });
52 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/raycaster/raycaster-camera.directive.ts:
--------------------------------------------------------------------------------
1 | import {AfterViewInit, Directive} from '@angular/core';
2 | import {AbstractCamera} from '../camera/abstract-camera';
3 | import {RaycasterService} from './raycaster.service';
4 |
5 | @Directive({selector: '[atft-raycaster-camera]'})
6 | export class RaycasterCameraDirective implements AfterViewInit {
7 |
8 | constructor(
9 | private host: AbstractCamera,
10 | private raycasterService: RaycasterService
11 | ) {
12 |
13 | }
14 |
15 | ngAfterViewInit(): void {
16 | this.raycasterService.setCamera(this.host);
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/raycaster/raycaster-enable.directive.spec.ts:
--------------------------------------------------------------------------------
1 | import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';
2 | import {RendererService} from '../renderer/renderer.service';
3 | import {Component} from '@angular/core';
4 | import {AnimationService} from '../animation';
5 | import {StatsService} from '../stats/stats.service';
6 | import {RaycasterService} from './raycaster.service';
7 | import {PerspectiveCameraComponent} from '../camera';
8 | import {RaycasterEnableDirective} from './raycaster-enable.directive';
9 |
10 | @Component({
11 | selector: 'atft-mock',
12 | template: `
13 |
14 | `
15 | })
16 | class MockComponent {
17 |
18 | }
19 |
20 | describe('raycaster', () => {
21 |
22 | describe('RaycasterEnableDirective', () => {
23 | let component: MockComponent;
24 | let fixture: ComponentFixture;
25 |
26 | beforeEach(waitForAsync(() => {
27 | TestBed.configureTestingModule({
28 | declarations: [
29 | PerspectiveCameraComponent,
30 | MockComponent,
31 | RaycasterEnableDirective
32 | ],
33 | providers: [
34 | StatsService,
35 | RendererService,
36 | AnimationService,
37 | RaycasterService
38 | ]
39 | });
40 | fixture = TestBed.createComponent(MockComponent);
41 | component = fixture.componentInstance;
42 | return TestBed.compileComponents();
43 | }));
44 |
45 | it('init', () => {
46 | fixture.detectChanges();
47 | expect(component).toBeTruthy();
48 | });
49 | });
50 |
51 | });
52 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/raycaster/raycaster-enable.directive.ts:
--------------------------------------------------------------------------------
1 | import {AfterViewInit, Directive} from '@angular/core';
2 | import {RaycasterService} from './raycaster.service';
3 |
4 | @Directive({selector: '[atft-raycaster-enable]'})
5 | export class RaycasterEnableDirective implements AfterViewInit {
6 |
7 | constructor(
8 | private raycasterService: RaycasterService
9 | ) {
10 |
11 | }
12 |
13 | ngAfterViewInit(): void {
14 | this.raycasterService.enable();
15 | }
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/raycaster/raycaster-event.ts:
--------------------------------------------------------------------------------
1 | export enum RaycasterEvent {
2 | mouseEnter = 'mouseEnter',
3 | mouseExit = 'mouseExit',
4 | click = 'click'
5 | }
6 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/raycaster/raycaster-group.directive.spec.ts:
--------------------------------------------------------------------------------
1 | import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';
2 | import {RendererService} from '../renderer/renderer.service';
3 | import {Component} from '@angular/core';
4 | import {AnimationService} from '../animation';
5 | import {StatsService} from '../stats/stats.service';
6 | import {RaycasterService} from './raycaster.service';
7 | import {EmptyComponent} from '../object/helper';
8 | import {RaycasterGroupDirective} from './raycaster-group.directive';
9 |
10 | @Component({
11 | selector: 'atft-mock',
12 | template: `
13 |
14 | `
15 | })
16 | class MockComponent {
17 |
18 | }
19 |
20 | describe('raycaster', () => {
21 |
22 | describe('RaycasterEnableDirective', () => {
23 | let component: MockComponent;
24 | let fixture: ComponentFixture;
25 |
26 | beforeEach(waitForAsync(() => {
27 | TestBed.configureTestingModule({
28 | declarations: [
29 | EmptyComponent,
30 | MockComponent,
31 | RaycasterGroupDirective
32 | ],
33 | providers: [
34 | StatsService,
35 | RendererService,
36 | AnimationService,
37 | RaycasterService
38 | ]
39 | });
40 | fixture = TestBed.createComponent(MockComponent);
41 | component = fixture.componentInstance;
42 | return TestBed.compileComponents();
43 | }));
44 |
45 | it('init', () => {
46 | fixture.detectChanges();
47 | expect(component).toBeTruthy();
48 | });
49 | });
50 |
51 | });
52 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/renderer/atft-renderer.module.ts:
--------------------------------------------------------------------------------
1 | import {NgModule} from '@angular/core';
2 | import {RendererCanvasComponent} from './renderer-canvas.component';
3 | import {CommonModule} from '@angular/common';
4 | import {RendererService} from './renderer.service';
5 | import {BloomService} from './bloom.service';
6 |
7 |
8 | @NgModule({
9 | imports: [
10 | CommonModule
11 | ],
12 | declarations: [
13 | RendererCanvasComponent
14 | ],
15 | providers: [
16 | RendererService,
17 | BloomService
18 | ],
19 | exports: [
20 | RendererCanvasComponent
21 | ]
22 | })
23 | export class AtftRendererModule {
24 | }
25 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/renderer/index.ts:
--------------------------------------------------------------------------------
1 | export * from './atft-renderer.module';
2 | export * from './renderer-canvas.component';
3 | export * from './renderer.service';
4 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/renderer/renderer-canvas.component.html:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/renderer/renderer-canvas.component.scss:
--------------------------------------------------------------------------------
1 | :host {
2 | display: flex;
3 | flex: 1;
4 | height: 100%;
5 | }
6 |
7 | canvas {
8 | flex: 1;
9 | outline: none;
10 | }
11 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/stats/index.ts:
--------------------------------------------------------------------------------
1 | export * from './stats.module';
2 | export * from './stats.service';
3 | export * from './stats-auto-show.directive';
4 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/stats/stats-auto-show.directive.ts:
--------------------------------------------------------------------------------
1 | import {AfterViewInit, Directive} from '@angular/core';
2 | import {StatsService} from './stats.service';
3 |
4 | @Directive({selector: '[atft-stats-auto-show]'})
5 | export class StatsAutoShowDirective implements AfterViewInit {
6 |
7 | constructor(
8 | private statsService: StatsService
9 | ) {
10 |
11 | }
12 |
13 | ngAfterViewInit(): void {
14 | this.statsService.create();
15 | }
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/stats/stats.module.ts:
--------------------------------------------------------------------------------
1 | import {NgModule} from '@angular/core';
2 | import {CommonModule} from '@angular/common';
3 | import {StatsService} from './stats.service';
4 | import {StatsAutoShowDirective} from './stats-auto-show.directive';
5 |
6 |
7 | @NgModule({
8 | imports: [
9 | CommonModule
10 | ],
11 | providers: [
12 | StatsService
13 | ],
14 | declarations: [
15 | StatsAutoShowDirective
16 | ],
17 | exports: [
18 | StatsAutoShowDirective
19 | ]
20 | })
21 | export class AtftStatsModule {
22 | }
23 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/stats/stats.service.spec.ts:
--------------------------------------------------------------------------------
1 | import {StatsService} from '../stats';
2 |
3 | describe('stats', () => {
4 | describe('StatsService', () => {
5 |
6 |
7 | it('create and remove', () => {
8 | const stats = new StatsService();
9 |
10 | const beforeCount = document.body.childElementCount;
11 | stats.create();
12 | expect(beforeCount + 1 === document.body.childElementCount).toBeTruthy();
13 |
14 | stats.update();
15 |
16 | stats.ngOnDestroy();
17 | expect(beforeCount === document.body.childElementCount).toBeTruthy();
18 |
19 | });
20 |
21 | it('toggle', () => {
22 | const stats = new StatsService();
23 |
24 | const beforeCount = document.body.childElementCount;
25 | stats.toggle();
26 | expect(beforeCount + 1 === document.body.childElementCount).toBeTruthy();
27 |
28 | stats.toggle();
29 | expect(beforeCount === document.body.childElementCount).toBeTruthy();
30 | });
31 |
32 | it('event', () => {
33 | const stats = new StatsService();
34 | stats.toggle();
35 |
36 | const beforeCount = document.body.childElementCount;
37 |
38 | const eventAltS = new KeyboardEvent('keydown', {altKey: true, key: 's'});
39 | document.body.dispatchEvent(eventAltS);
40 | expect(document.body.childElementCount).toBeGreaterThan(beforeCount);
41 |
42 | document.body.dispatchEvent(eventAltS);
43 | expect(beforeCount === document.body.childElementCount).toBeTruthy();
44 | });
45 |
46 | });
47 |
48 | });
49 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/stats/stats.service.ts:
--------------------------------------------------------------------------------
1 | import {Injectable, OnDestroy} from '@angular/core';
2 | import Stats from 'three/examples/jsm/libs/stats.module.js';
3 |
4 |
5 | @Injectable()
6 | export class StatsService implements OnDestroy {
7 |
8 | private stats?: Stats;
9 |
10 | constructor() {
11 | document.body.addEventListener('keydown', event => {
12 | if (event.altKey && event.key === 's') {
13 | this.toggle();
14 | }
15 | });
16 | }
17 |
18 | public update() {
19 | if (this.stats) {
20 | this.stats.update();
21 | }
22 | }
23 |
24 |
25 | public toggle() {
26 | (this.stats ? this.remove() : this.create());
27 | }
28 |
29 | public create() {
30 | if (!this.stats) {
31 | this.stats = new Stats();
32 | document.body.appendChild(this.stats.dom);
33 | }
34 | }
35 |
36 | public remove() {
37 | if (this.stats) {
38 | this.stats.dom.remove();
39 | // @ts-ignore
40 | this.stats.dom = undefined;
41 | this.stats = undefined;
42 | }
43 | }
44 |
45 | ngOnDestroy(): void {
46 | this.remove();
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/threejs-fork/index.ts:
--------------------------------------------------------------------------------
1 | export * from './SVGLoader';
2 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/util/applied-material.spec.ts:
--------------------------------------------------------------------------------
1 | import {appliedMaterial} from './applied-material';
2 | import * as THREE from 'three';
3 |
4 | describe('utils', () => {
5 | describe('appliedMaterial', () => {
6 |
7 | it('default', () => {
8 | const material = appliedMaterial(0xff0000);
9 | expect(material.constructor).toBe(THREE.MeshPhongMaterial);
10 | expect(material.depthWrite).toBeTruthy();
11 | });
12 |
13 | it('basic', () => {
14 | const material = appliedMaterial(0xff0000, 'basic');
15 | expect(material.constructor).toBe(THREE.MeshBasicMaterial);
16 | });
17 |
18 | it('lambert', () => {
19 | const material = appliedMaterial(0xff0000, 'lamb', true);
20 | expect(material.constructor).toBe(THREE.MeshLambertMaterial);
21 | expect(material.depthWrite).toBeTruthy();
22 | });
23 |
24 | it('lambert depth', () => {
25 | const material = appliedMaterial(0xff0000, 'lamb', false);
26 | expect(material.constructor).toBe(THREE.MeshLambertMaterial);
27 | expect(material.depthWrite).toBeFalsy();
28 | });
29 |
30 | it('Color object', () => {
31 | const material = appliedMaterial(new THREE.Color(1, 0, 0));
32 | expect(material.constructor).toBe(THREE.MeshPhongMaterial);
33 | });
34 |
35 | it('Color hex string', () => {
36 | const material = appliedMaterial('#ff0000');
37 | expect(material.constructor).toBe(THREE.MeshPhongMaterial);
38 | expect(material.depthWrite).toBeTruthy();
39 | });
40 |
41 | });
42 |
43 | });
44 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/util/applied-material.ts:
--------------------------------------------------------------------------------
1 | import * as THREE from 'three';
2 |
3 | /**
4 | * Creates material
5 | *
6 | * @param materialColor color
7 | * @param materialName material label as string
8 | * @param depthWrite enable depth write
9 | */
10 | export function appliedMaterial(materialColor: string | number | THREE.Color, materialName?: string, depthWrite?: boolean): THREE.Material {
11 |
12 | const props = {
13 | color: (materialColor instanceof THREE.Color ? materialColor : new THREE.Color(materialColor)),
14 | side: THREE.DoubleSide,
15 | depthWrite: (depthWrite !== undefined ? depthWrite : true)
16 | };
17 |
18 | if (materialName === 'lamb') {
19 | return new THREE.MeshLambertMaterial(props);
20 | } else if (materialName === 'basic') {
21 | return new THREE.MeshBasicMaterial(props);
22 | } else {
23 | return new THREE.MeshPhongMaterial(props);
24 | }
25 |
26 | }
27 |
28 |
29 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/util/calculate-size.spec.ts:
--------------------------------------------------------------------------------
1 | import {calculateSize} from './calculate-size';
2 | import * as THREE from 'three';
3 |
4 | describe('utils', () => {
5 | describe('calculateSize', () => {
6 |
7 | it('scaled box', () => {
8 | const geometry = new THREE.BoxGeometry(2, 3, 4);
9 | const obj = new THREE.Mesh(geometry);
10 | obj.scale.x = 2;
11 | obj.scale.y = 3;
12 | obj.scale.z = 4;
13 |
14 | const vector = calculateSize(obj);
15 | expect(vector.x).toEqual(2 * 2);
16 | expect(vector.y).toEqual(3 * 3);
17 | expect(vector.z).toEqual(4 * 4);
18 | });
19 |
20 |
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/util/calculate-size.ts:
--------------------------------------------------------------------------------
1 | import * as THREE from 'three';
2 |
3 | export function calculateSize(group: THREE.Object3D): THREE.Vector3 {
4 | const box = new THREE.Box3().setFromObject(group);
5 | return new THREE.Vector3(
6 | box.max.x - box.min.x,
7 | box.max.y - box.min.y,
8 | box.max.z - box.min.z
9 | );
10 | }
11 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/util/fix-center.spec.ts:
--------------------------------------------------------------------------------
1 | import * as THREE from 'three';
2 | import {fixCenter} from './fix-center';
3 |
4 | describe('utils', () => {
5 | describe('fixCenter', () => {
6 |
7 | it('simple', () => {
8 | const geometry = new THREE.BoxGeometry(4, 1, 2);
9 | const obj = new THREE.Mesh(geometry);
10 | fixCenter(obj);
11 |
12 | expect(obj.position.x).toEqual(0);
13 | expect(obj.position.y).toEqual(0);
14 | expect(obj.position.z).toEqual(0);
15 | });
16 |
17 | it('group', () => {
18 | const geometry = new THREE.BoxGeometry(1, 1, 1);
19 | const group = new THREE.Group();
20 | const obj = new THREE.Mesh(geometry);
21 | obj.position.x = 2;
22 | obj.position.y = -2;
23 | obj.position.z = 0;
24 | group.add(obj);
25 | fixCenter(group);
26 |
27 | expect(obj.position.x).toEqual(2);
28 | expect(obj.position.y).toEqual(-2);
29 | expect(obj.position.z).toEqual(0);
30 | });
31 |
32 |
33 | });
34 | });
35 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/util/fix-center.ts:
--------------------------------------------------------------------------------
1 | import * as THREE from 'three';
2 |
3 | export function fixCenter(group: THREE.Object3D) {
4 | const box = new THREE.Box3().setFromObject(group);
5 | // console.log('box', box);
6 | group.translateX(-((box.max.x - box.min.x) / 2) - box.min.x);
7 | group.translateY(-((box.max.y - box.min.y) / 2) - box.min.y);
8 | group.translateZ(-((box.max.z - box.min.z) / 2) - box.min.z);
9 | }
10 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/util/index.ts:
--------------------------------------------------------------------------------
1 | export * from './applied-material';
2 | export * from './calculate-size';
3 | export * from './fix-center';
4 | export * from './scale-to-fit';
5 | export * from './provide-parent';
6 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/util/provide-parent.ts:
--------------------------------------------------------------------------------
1 | // https://angular.io/guide/dependency-injection-navtree
2 | // Helper method to provide the current component instance in the name of a `parentType`.
3 | // The `parentType` defaults to `Parent` when omitting the second parameter.
4 | import { forwardRef } from '@angular/core';
5 | import { AbstractObject3D } from '../object/abstract-object-3d';
6 |
7 | export function provideParent(component: any, parentType?: any) {
8 | return { provide: parentType || AbstractObject3D, useExisting: forwardRef(() => component) };
9 | }
10 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/util/removeFromArray.ts:
--------------------------------------------------------------------------------
1 | export function removeFromArray(array: Array, element: T): void {
2 | const index = array.indexOf(element, 0);
3 | if (index > -1) {
4 | array.splice(index, 1);
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/util/scale-to-fit.spec.ts:
--------------------------------------------------------------------------------
1 | import * as THREE from 'three';
2 | import {scaleToFit} from './scale-to-fit';
3 |
4 | describe('utils', () => {
5 | describe('scaleToFit', () => {
6 |
7 | it('simple', () => {
8 | const geometry = new THREE.BoxGeometry(10, 20, 30);
9 | const obj = new THREE.Mesh(geometry);
10 |
11 | const max = new THREE.Vector3(20, 10, 30);
12 | scaleToFit(obj, max);
13 |
14 | expect(obj.scale.x).toEqual(1);
15 | expect(obj.scale.y).toEqual(0.5);
16 | expect(obj.scale.z).toEqual(1);
17 | });
18 |
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/projects/atft/src/lib/util/scale-to-fit.ts:
--------------------------------------------------------------------------------
1 | import * as THREE from 'three';
2 | import {calculateSize} from './calculate-size';
3 |
4 | export function scaleToFit(group: THREE.Object3D, max: THREE.Vector3): void {
5 | const box = calculateSize(group);
6 |
7 | const scaleX = max.x / box.x;
8 | const scaleY = max.y / box.y;
9 | const scaleZ = max.z / box.z;
10 |
11 | group.scale.set(
12 | (scaleX < 1 ? scaleX : 1),
13 | (scaleY < 1 ? scaleY : 1),
14 | (scaleZ < 1 ? scaleZ : 1)
15 | );
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/projects/atft/src/public_api.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Public API Surface of atft
3 | */
4 |
5 | export * from './lib/atft.module';
6 | export * from './lib/camera';
7 | export * from './lib/control';
8 | export * from './lib/object';
9 | export * from './lib/pipe';
10 | export * from './lib/renderer';
11 | export * from './lib/util';
12 | export * from './lib/animation';
13 | export * from './lib/raycaster';
14 | export * from './lib/stats';
15 | export * from './lib/effect';
16 | export * from './lib/threejs-fork';
17 |
18 | export * from './lib/actor/data-center/';
19 | export * from './lib/actor/ux/';
20 |
--------------------------------------------------------------------------------
/projects/atft/src/test.ts:
--------------------------------------------------------------------------------
1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files
2 |
3 | import 'core-js/es7/reflect';
4 | import 'zone.js/dist/zone';
5 | import 'zone.js/dist/zone-testing';
6 | import {getTestBed} from '@angular/core/testing';
7 | import {BrowserDynamicTestingModule, platformBrowserDynamicTesting} from '@angular/platform-browser-dynamic/testing';
8 |
9 | declare const require: any;
10 |
11 | // First, initialize the Angular testing environment.
12 | getTestBed().initTestEnvironment(
13 | BrowserDynamicTestingModule,
14 | platformBrowserDynamicTesting()
15 | );
16 | // Then we find all the tests.
17 | const context = require.context('./', true, /\.ts$/);
18 | // And load the modules.
19 | context.keys().map(context);
20 |
--------------------------------------------------------------------------------
/projects/atft/tsconfig.lib.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "extends": "../../tsconfig.json",
4 | "compilerOptions": {
5 | "outDir": "../../out-tsc/lib",
6 | "declaration": true,
7 | "declarationMap": true,
8 | "inlineSources": true,
9 | "types": []
10 | },
11 | "exclude": [
12 | "**/*.spec.ts"
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/projects/atft/tsconfig.lib.prod.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "extends": "./tsconfig.lib.json",
4 | "compilerOptions": {
5 | "declarationMap": false
6 | },
7 | "angularCompilerOptions": {
8 | "compilationMode": "partial"
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/projects/atft/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "extends": "../../tsconfig.json",
4 | "compilerOptions": {
5 | "outDir": "../../out-tsc/spec",
6 | "types": [
7 | "jasmine"
8 | ]
9 | },
10 | "include": [
11 | "**/*.spec.ts",
12 | "**/*.ts"
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/src/app/app-routing.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { RouterModule, Routes } from '@angular/router';
3 |
4 | const routes: Routes = [];
5 |
6 | @NgModule({
7 | imports: [RouterModule.forRoot(routes)],
8 | exports: [RouterModule]
9 | })
10 | export class AppRoutingModule { }
11 |
--------------------------------------------------------------------------------
/src/app/app.component.scss:
--------------------------------------------------------------------------------
1 | :host {
2 | width: 100%;
3 | height: 100%;
4 | max-height: 100%;
5 | display: flex;
6 | }
7 |
--------------------------------------------------------------------------------
/src/app/app.component.spec.ts:
--------------------------------------------------------------------------------
1 | import {TestBed, waitForAsync} from '@angular/core/testing';
2 | import {AppComponent} from './app.component';
3 |
4 | import {FormsModule} from '@angular/forms';
5 | import {AtftModule} from 'atft';
6 |
7 | describe('AppComponent', () => {
8 | beforeEach(waitForAsync(() => {
9 | TestBed.configureTestingModule({
10 | declarations: [
11 | AppComponent
12 | ],
13 | imports: [
14 | FormsModule,
15 | AtftModule
16 | ]
17 | }).compileComponents();
18 | }));
19 | it('should create the app', waitForAsync(() => {
20 | const fixture = TestBed.createComponent(AppComponent);
21 | const app = fixture.debugElement.componentInstance;
22 | expect(app).toBeTruthy();
23 | }));
24 | it(`should have as title 'app'`, waitForAsync(() => {
25 | const fixture = TestBed.createComponent(AppComponent);
26 | const app = fixture.debugElement.componentInstance;
27 | expect(app.title).toEqual('app');
28 | }));
29 | it('should create