UI: Battery, Light
This commit is contained in:
commit
b0038fd7a7
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/target/
|
||||
18
pom.xml
Normal file
18
pom.xml
Normal file
@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>de.ph87.data</groupId>
|
||||
<artifactId>Elektro</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
<maven.compiler.release>21</maven.compiler.release>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
</project>
|
||||
18
src/main/angular/.editorconfig
Normal file
18
src/main/angular/.editorconfig
Normal file
@ -0,0 +1,18 @@
|
||||
# Editor configuration, see https://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.ts]
|
||||
# noinspection EditorConfigKeyCorrectness
|
||||
quote_type = single
|
||||
ij_typescript_use_double_quotes = false
|
||||
|
||||
[*.md]
|
||||
max_line_length = off
|
||||
trim_trailing_whitespace = false
|
||||
42
src/main/angular/.gitignore
vendored
Normal file
42
src/main/angular/.gitignore
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
# See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files.
|
||||
|
||||
# Compiled output
|
||||
/dist
|
||||
/tmp
|
||||
/out-tsc
|
||||
/bazel-out
|
||||
|
||||
# Node
|
||||
/node_modules
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
|
||||
# IDEs and editors
|
||||
.idea/
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-workspace
|
||||
|
||||
# Visual Studio Code
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
.history/*
|
||||
|
||||
# Miscellaneous
|
||||
/.angular/cache
|
||||
.sass-cache/
|
||||
/connect.lock
|
||||
/coverage
|
||||
/libpeerconnection.log
|
||||
testem.log
|
||||
/typings
|
||||
|
||||
# System files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
59
src/main/angular/README.md
Normal file
59
src/main/angular/README.md
Normal file
@ -0,0 +1,59 @@
|
||||
# Angular
|
||||
|
||||
This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 19.0.7.
|
||||
|
||||
## Development server
|
||||
|
||||
To start a local development server, run:
|
||||
|
||||
```bash
|
||||
ng serve
|
||||
```
|
||||
|
||||
Once the server is running, open your browser and navigate to `http://localhost:4200/`. The application will automatically reload whenever you modify any of the source files.
|
||||
|
||||
## Code scaffolding
|
||||
|
||||
Angular CLI includes powerful code scaffolding tools. To generate a new component, run:
|
||||
|
||||
```bash
|
||||
ng generate component component-name
|
||||
```
|
||||
|
||||
For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run:
|
||||
|
||||
```bash
|
||||
ng generate --help
|
||||
```
|
||||
|
||||
## Building
|
||||
|
||||
To build the project run:
|
||||
|
||||
```bash
|
||||
ng build
|
||||
```
|
||||
|
||||
This will compile your project and store the build artifacts in the `dist/` directory. By default, the production build optimizes your application for performance and speed.
|
||||
|
||||
## Running unit tests
|
||||
|
||||
To execute unit tests with the [Karma](https://karma-runner.github.io) test runner, use the following command:
|
||||
|
||||
```bash
|
||||
ng test
|
||||
```
|
||||
|
||||
## Running end-to-end tests
|
||||
|
||||
For end-to-end (e2e) testing, run:
|
||||
|
||||
```bash
|
||||
ng e2e
|
||||
```
|
||||
|
||||
Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.
|
||||
|
||||
## Additional Resources
|
||||
|
||||
For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.
|
||||
124
src/main/angular/angular.json
Normal file
124
src/main/angular/angular.json
Normal file
@ -0,0 +1,124 @@
|
||||
{
|
||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||
"version": 1,
|
||||
"newProjectRoot": "projects",
|
||||
"projects": {
|
||||
"angular": {
|
||||
"projectType": "application",
|
||||
"schematics": {
|
||||
"@schematics/angular:component": {
|
||||
"style": "less",
|
||||
"skipTests": true
|
||||
},
|
||||
"@schematics/angular:class": {
|
||||
"skipTests": true
|
||||
},
|
||||
"@schematics/angular:directive": {
|
||||
"skipTests": true
|
||||
},
|
||||
"@schematics/angular:guard": {
|
||||
"skipTests": true
|
||||
},
|
||||
"@schematics/angular:interceptor": {
|
||||
"skipTests": true
|
||||
},
|
||||
"@schematics/angular:pipe": {
|
||||
"skipTests": true
|
||||
},
|
||||
"@schematics/angular:resolver": {
|
||||
"skipTests": true
|
||||
},
|
||||
"@schematics/angular:service": {
|
||||
"skipTests": true
|
||||
}
|
||||
},
|
||||
"root": "",
|
||||
"sourceRoot": "src",
|
||||
"prefix": "app",
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:application",
|
||||
"options": {
|
||||
"outputPath": "dist/angular",
|
||||
"index": "src/index.html",
|
||||
"browser": "src/main.ts",
|
||||
"polyfills": [
|
||||
"zone.js"
|
||||
],
|
||||
"tsConfig": "tsconfig.app.json",
|
||||
"inlineStyleLanguage": "less",
|
||||
"assets": [
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "public"
|
||||
}
|
||||
],
|
||||
"styles": [
|
||||
"src/styles.less"
|
||||
],
|
||||
"scripts": []
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "500kB",
|
||||
"maximumError": "1MB"
|
||||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "4kB",
|
||||
"maximumError": "8kB"
|
||||
}
|
||||
],
|
||||
"outputHashing": "all"
|
||||
},
|
||||
"development": {
|
||||
"optimization": false,
|
||||
"extractLicenses": false,
|
||||
"sourceMap": true
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "production"
|
||||
},
|
||||
"serve": {
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
"configurations": {
|
||||
"production": {
|
||||
"buildTarget": "angular:build:production"
|
||||
},
|
||||
"development": {
|
||||
"buildTarget": "angular:build:development"
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "development"
|
||||
},
|
||||
"extract-i18n": {
|
||||
"builder": "@angular-devkit/build-angular:extract-i18n"
|
||||
},
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
"options": {
|
||||
"polyfills": [
|
||||
"zone.js",
|
||||
"zone.js/testing"
|
||||
],
|
||||
"tsConfig": "tsconfig.spec.json",
|
||||
"inlineStyleLanguage": "less",
|
||||
"assets": [
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "public"
|
||||
}
|
||||
],
|
||||
"styles": [
|
||||
"src/styles.less"
|
||||
],
|
||||
"scripts": []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
14401
src/main/angular/package-lock.json
generated
Normal file
14401
src/main/angular/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
38
src/main/angular/package.json
Normal file
38
src/main/angular/package.json
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
"name": "angular",
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"start": "ng serve",
|
||||
"build": "ng build",
|
||||
"watch": "ng build --watch --configuration development",
|
||||
"test": "ng test"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "^19.0.0",
|
||||
"@angular/common": "^19.0.0",
|
||||
"@angular/compiler": "^19.0.0",
|
||||
"@angular/core": "^19.0.0",
|
||||
"@angular/forms": "^19.0.0",
|
||||
"@angular/platform-browser": "^19.0.0",
|
||||
"@angular/platform-browser-dynamic": "^19.0.0",
|
||||
"@angular/router": "^19.0.0",
|
||||
"rxjs": "~7.8.0",
|
||||
"tslib": "^2.3.0",
|
||||
"zone.js": "~0.15.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^19.0.7",
|
||||
"@angular/cli": "^19.0.7",
|
||||
"@angular/compiler-cli": "^19.0.0",
|
||||
"@types/jasmine": "~5.1.0",
|
||||
"jasmine-core": "~5.4.0",
|
||||
"karma": "~6.4.0",
|
||||
"karma-chrome-launcher": "~3.2.0",
|
||||
"karma-coverage": "~2.2.0",
|
||||
"karma-jasmine": "~5.1.0",
|
||||
"karma-jasmine-html-reporter": "~2.1.0",
|
||||
"typescript": "~5.6.2"
|
||||
}
|
||||
}
|
||||
19
src/main/angular/public/favicon.svg
Normal file
19
src/main/angular/public/favicon.svg
Normal file
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 398.502 398.502">
|
||||
<polygon fill="#3E81C8" points="227.741,360.947 221.728,386.947 176.754,386.947 170.741,360.947"/>
|
||||
<polygon fill="#3E81C8" points="254.751,325.177 248.067,353.177 150.435,353.177 143.751,325.177"/>
|
||||
<rect x="167.751" y="254.827" fill="#EB7830" width="63" height="63.74"/>
|
||||
<path fill="#EB7830" d="M237.161,193.872l-6.44,55.56h-62.94l-6.44-55.56l7.5,3.91c1.57,1.45,8.66,2.2,10.79,2.11c2.13-0.1,4.13-1.04,5.56-2.62l14.06-12.53l14.05,12.53c1.43,1.58,3.44,2.52,5.57,2.62c0.12,0,0.24,0.01,0.36,0.01c2.01,0,8.94-0.75,10.42-2.12L237.161,193.872z"/>
|
||||
<path fill="#F5C525" d="M297.261,176.372c0,14.23-3.12,27.88-9.29,40.57c-14.55,29.97-28.07,67.25-29.42,101.02h-22.34v-65.53l8.93-65.49c0.62-4.37,3.58-8.42-0.8-9.04c-2.47-0.35-4.84,0.47-6.54,2.04l-18.05,16.61l-14.57-16.1c-1.52-1.67-3.67-2.63-5.93-2.63s-4.42,0.96-5.93,2.63l-14.58,16.1l-18.04-16.62c-3.25-2.99-3.31-2.78-6.31,0.47c-1.69,1.84-2.36,4.25-2.03,6.54l9.93,65.49v65.53h-22.35c-1.14-28.07-10.35-63.35-28.73-99.64c-6.57-12.98-9.93-26.99-9.97-41.65c-0.16-50.74,45.97-97.6,96.68-98.3C249.831,77.662,297.261,124.642,297.261,176.372z"/>
|
||||
<path fill="#231F20"
|
||||
d="M199.251,67.362c-0.52,0-1.03,0-1.55,0.01c-59.44,0.82-107.65,49.88-107.46,109.35c0.05,17.18,3.99,33.6,11.7,48.83c18.5,36.55,27.12,66.21,27.12,93.36v2.05c0,0.8,0.13,1.58,0.35,2.31c0,0,0,0,0,0.01v0.01c0.01,0.03,0.02,0.05,0.03,0.08l11.01,36.34c1.02,3.37,4.13,5.68,7.65,5.68h10.23l8.19,27.4c1.01,3.39,4.13,5.71,7.66,5.71h50.14c3.53,0,6.65-2.32,7.66-5.71l8.19-27.4h10.23c3.52,0,6.63-2.31,7.65-5.68l11.02-36.34c0-0.03,0.01-0.05,0.02-0.08v-0.01c0-0.01,0-0.01,0-0.01c0.22-0.73,0.35-1.51,0.35-2.31v-2.51c0-32.53,13.42-64.65,27.92-94.52c7.24-14.89,10.9-30.89,10.9-47.56C308.261,116.262,259.361,67.362,199.251,67.362z M218.361,382.502h-38.22l-5.11-17.11h48.44L218.361,382.502z M244.461,349.392h-90.42l-6.19-20.43h102.8L244.461,349.392z M172.781,244.432l-6.44-45.56l7.5,6.91c1.57,1.45,3.66,2.2,5.79,2.11c2.13-0.1,4.13-1.04,5.56-2.62l14.06-15.53l14.05,15.53c1.43,1.58,3.44,2.52,5.57,2.62c0.12,0,0.24,0.01,0.36,0.01c2.01,0,3.94-0.75,5.42-2.12l7.51-6.91l-6.44,45.56H172.781z M225.211,260.432v52.53h-51.92v-52.53H225.211z M282.971,216.942c-14.55,29.97-28.07,62.25-29.42,96.02h-12.34v-60.53l9.93-73.49c0.62-4.37-2.42-8.42-6.8-9.04c-2.47-0.35-4.84,0.47-6.54,2.04l-18.05,16.61l-14.57-16.1c-1.52-1.67-3.67-2.63-5.93-2.63s-4.42,0.96-5.93,2.63l-14.58,16.1l-18.04-16.62c-3.25-2.99-8.31-2.78-11.31,0.47c-1.69,1.84-2.36,4.25-2.03,6.54l9.93,73.49v60.53h-12.35c-1.14-28.07-10.35-58.35-28.73-94.64c-6.57-12.98-9.93-26.99-9.97-41.65c-0.16-50.74,40.97-92.6,91.68-93.3c51.91-0.71,94.34,41.27,94.34,93C292.261,190.602,289.141,204.252,282.971,216.942z"/>
|
||||
<path fill="#231F20" d="M67.602,175.614c0-4.418-3.582-8-8-8H31.637c-4.418,0-8,3.582-8,8s3.582,8,8,8h27.965C64.021,183.614,67.602,180.032,67.602,175.614z"/>
|
||||
<path fill="#231F20" d="M366.865,167.614H338.9c-4.418,0-8,3.582-8,8s3.582,8,8,8h27.965c4.418,0,8-3.582,8-8S371.284,167.614,366.865,167.614z"/>
|
||||
<path fill="#231F20" d="M82.311,98.861L58.093,84.878c-3.827-2.21-8.719-0.898-10.928,2.928c-2.209,3.826-0.898,8.719,2.928,10.928l24.218,13.983c1.26,0.728,2.635,1.073,3.993,1.073c2.765,0,5.454-1.435,6.936-4.001C87.449,105.963,86.138,101.071,82.311,98.861z"/>
|
||||
<path fill="#231F20" d="M348.409,252.493L324.19,238.51c-3.827-2.209-8.719-0.898-10.928,2.928c-2.209,3.826-0.898,8.719,2.928,10.928l24.219,13.983c1.26,0.728,2.635,1.073,3.993,1.073c2.765,0,5.454-1.435,6.936-4.001C353.546,259.595,352.235,254.702,348.409,252.493z"/>
|
||||
<path fill="#231F20" d="M122.498,58.674c1.482,2.566,4.171,4.001,6.936,4.001c1.357,0,2.733-0.346,3.993-1.073c3.826-2.209,5.137-7.102,2.928-10.928l-13.982-24.218c-2.209-3.826-7.103-5.135-10.928-2.928c-3.826,2.209-5.137,7.102-2.928,10.928L122.498,58.674z"/>
|
||||
<path fill="#231F20" d="M199.251,43.965c4.418,0,8-3.582,8-8V8c0-4.418-3.582-8-8-8s-8,3.582-8,8v27.965C191.251,40.384,194.832,43.965,199.251,43.965z"/>
|
||||
<path fill="#231F20" d="M265.076,61.603c1.26,0.728,2.635,1.073,3.993,1.073c2.765,0,5.454-1.435,6.936-4.001l13.982-24.218c2.209-3.826,0.898-8.719-2.928-10.928c-3.826-2.21-8.719-0.898-10.928,2.928l-13.982,24.218C259.938,54.5,261.249,59.394,265.076,61.603z"/>
|
||||
<path fill="#231F20" d="M320.198,113.791c1.357,0,2.733-0.346,3.993-1.073l24.219-13.983c3.826-2.209,5.137-7.102,2.928-10.928c-2.209-3.827-7.103-5.135-10.928-2.928l-24.22,13.982c-3.826,2.209-5.137,7.102-2.928,10.928C314.744,112.356,317.433,113.791,320.198,113.791z"/>
|
||||
<path fill="#231F20" d="M74.311,238.51l-24.218,13.983c-3.827,2.209-5.137,7.102-2.928,10.928c1.482,2.567,4.171,4.001,6.936,4.001c1.357,0,2.733-0.346,3.993-1.073l24.218-13.983c3.826-2.209,5.137-7.102,2.928-10.928C83.03,237.611,78.137,236.303,74.311,238.51z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.7 KiB |
1
src/main/angular/src/app/app.component.html
Normal file
1
src/main/angular/src/app/app.component.html
Normal file
@ -0,0 +1 @@
|
||||
<router-outlet/>
|
||||
0
src/main/angular/src/app/app.component.less
Normal file
0
src/main/angular/src/app/app.component.less
Normal file
11
src/main/angular/src/app/app.component.ts
Normal file
11
src/main/angular/src/app/app.component.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import {Component} from '@angular/core';
|
||||
import {RouterOutlet} from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
imports: [RouterOutlet],
|
||||
templateUrl: './app.component.html',
|
||||
styleUrl: './app.component.less'
|
||||
})
|
||||
export class AppComponent {
|
||||
}
|
||||
8
src/main/angular/src/app/app.config.ts
Normal file
8
src/main/angular/src/app/app.config.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import {ApplicationConfig, provideZoneChangeDetection} from '@angular/core';
|
||||
import {provideRouter} from '@angular/router';
|
||||
|
||||
import {routes} from './app.routes';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [provideZoneChangeDetection({eventCoalescing: true}), provideRouter(routes)]
|
||||
};
|
||||
7
src/main/angular/src/app/app.routes.ts
Normal file
7
src/main/angular/src/app/app.routes.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import {Routes} from '@angular/router';
|
||||
import {EditorComponent} from './editor/editor.component';
|
||||
|
||||
export const routes: Routes = [
|
||||
{path: 'Editor', component: EditorComponent},
|
||||
{path: '**', redirectTo: 'Editor'},
|
||||
];
|
||||
@ -0,0 +1,32 @@
|
||||
<svg class="breadboard">
|
||||
|
||||
<svg *ngFor="let part of parts" [attr.height]="part.hP" [attr.width]="part.wP" [attr.x]="part.xP" [attr.y]="part.yP" [ngClass]="part.ngClass()">
|
||||
|
||||
<rect class="background" height="100%" width="100%" x="0" y="0"></rect>
|
||||
|
||||
<ng-container *ngIf="isBattery(part)">
|
||||
<text dominant-baseline="hanging" text-anchor="middle" x="50%" y="3%">{{ asBattery(part).voltageStr }}</text>
|
||||
<line class="wire" id="wireMinus" x1="15%" x2="40%" y1="50%" y2="50%"></line>
|
||||
<line class="wire" id="wirePlus" x1="55%" x2="80%" y1="50%" y2="50%"></line>
|
||||
<rect height="30%" id="symbolMinus" width="10%" x="35%" y="35%"></rect>
|
||||
<rect height="60%" id="symbolPlus" width="5%" x="55%" y="20%"></rect>
|
||||
<text text-anchor="middle" x="50%" y="95%">{{ asBattery(part).currentStr }}</text>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="isLight(part)">
|
||||
<text dominant-baseline="hanging" text-anchor="middle" x="50%" y="3%">{{ asBattery(part).voltageStr }}</text>
|
||||
<circle [attr.fill]="asLight(part).fill()" [class.defect]="asLight(part).defect" cx="50%" cy="50%" id="circle" r="35%"></circle>
|
||||
<g class="cross">
|
||||
<line id="lineLeftRight" x1="15%" x2="85%" y1="50%" y2="50%"></line>
|
||||
<line id="lineTopDown" x1="50%" x2="50%" y1="15%" y2="85%"></line>
|
||||
</g>
|
||||
<text text-anchor="middle" x="50%" y="95%">{{ asBattery(part).currentStr }}</text>
|
||||
</ng-container>
|
||||
|
||||
<svg *ngFor="let j of part.junctions" [attr.height]="j.hP" [attr.width]="j.wP" [attr.x]="j.xP" [attr.y]="j.yP" class="junction">
|
||||
<circle [attr.id]="'j' + j.name" cx="50%" cy="50%" fill="gray" r="35%"></circle>
|
||||
</svg>
|
||||
|
||||
</svg>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
@ -0,0 +1,59 @@
|
||||
.breadboard {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
-webkit-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
|
||||
.part {
|
||||
.background {
|
||||
fill: lightgray;
|
||||
}
|
||||
}
|
||||
|
||||
.part:not(:has(.junction:hover)):hover {
|
||||
.background {
|
||||
fill: lightblue;
|
||||
}
|
||||
}
|
||||
|
||||
.partLight {
|
||||
|
||||
circle {
|
||||
stroke-width: 1px;
|
||||
stroke: black;
|
||||
}
|
||||
|
||||
.defect {
|
||||
filter: drop-shadow(0 0 30px black);
|
||||
}
|
||||
|
||||
.cross {
|
||||
transform-origin: 50% 50%;
|
||||
transform: rotate(45deg);
|
||||
stroke-width: 1px;
|
||||
stroke: black;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.wire {
|
||||
stroke-width: 5px;
|
||||
stroke: black;
|
||||
}
|
||||
|
||||
.junction {
|
||||
stroke-width: 1px;
|
||||
stroke: black;
|
||||
}
|
||||
|
||||
.junction:hover {
|
||||
circle {
|
||||
fill: lightblue;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,40 @@
|
||||
import {Component} from '@angular/core';
|
||||
import {NgClass, NgForOf, NgIf} from '@angular/common';
|
||||
import {Part} from '../parts/Part';
|
||||
import {Battery} from '../parts/battery/Battery';
|
||||
import {Light} from '../parts/light/Light';
|
||||
|
||||
@Component({
|
||||
selector: 'app-board',
|
||||
imports: [
|
||||
NgForOf,
|
||||
NgIf,
|
||||
NgClass,
|
||||
],
|
||||
templateUrl: './breadboard.component.html',
|
||||
styleUrl: './breadboard.component.less'
|
||||
})
|
||||
export class BreadboardComponent {
|
||||
|
||||
parts: Part[] = [
|
||||
new Battery(1, 1, 3, 0.5),
|
||||
new Light(1, 5, 3, 3, 0.5),
|
||||
];
|
||||
|
||||
asBattery(part: Part): Battery {
|
||||
return part as Battery;
|
||||
}
|
||||
|
||||
isBattery(part: Part): boolean {
|
||||
return part instanceof Battery;
|
||||
}
|
||||
|
||||
asLight(part: Part): Light {
|
||||
return part as Light;
|
||||
}
|
||||
|
||||
isLight(part: Part): boolean {
|
||||
return part instanceof Light;
|
||||
}
|
||||
|
||||
}
|
||||
1
src/main/angular/src/app/editor/editor.component.html
Normal file
1
src/main/angular/src/app/editor/editor.component.html
Normal file
@ -0,0 +1 @@
|
||||
<app-board></app-board>
|
||||
14
src/main/angular/src/app/editor/editor.component.ts
Normal file
14
src/main/angular/src/app/editor/editor.component.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import {Component} from '@angular/core';
|
||||
import {BreadboardComponent} from './breadboard/breadboard.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-editor',
|
||||
imports: [
|
||||
BreadboardComponent
|
||||
],
|
||||
templateUrl: './editor.component.html',
|
||||
styleUrl: './editor.component.less'
|
||||
})
|
||||
export class EditorComponent {
|
||||
|
||||
}
|
||||
11
src/main/angular/src/app/editor/fadeGrayToYellow.ts
Normal file
11
src/main/angular/src/app/editor/fadeGrayToYellow.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import {parseColor} from './parseColor';
|
||||
|
||||
export function fadeGrayToYellow(factor: number, fromColor: any, toColor: any): string {
|
||||
fromColor = parseColor(fromColor);
|
||||
toColor = parseColor(toColor);
|
||||
factor = Math.max(0, Math.min(100, factor));
|
||||
const r = Math.round(fromColor.r + factor * (toColor.r - fromColor.r));
|
||||
const g = Math.round(fromColor.g + factor * (toColor.g - fromColor.g));
|
||||
const b = Math.round(fromColor.b + factor * (toColor.b - fromColor.b));
|
||||
return `rgb(${r}, ${g}, ${b})`;
|
||||
}
|
||||
29
src/main/angular/src/app/editor/junction/Junction.ts
Normal file
29
src/main/angular/src/app/editor/junction/Junction.ts
Normal file
@ -0,0 +1,29 @@
|
||||
export const JUNCTION_RADIUS_PERCENT = 15;
|
||||
|
||||
export class Junction {
|
||||
|
||||
constructor(
|
||||
readonly x: number,
|
||||
readonly y: number,
|
||||
readonly name: string,
|
||||
) {
|
||||
//
|
||||
}
|
||||
|
||||
get xP(): string {
|
||||
return (this.x - JUNCTION_RADIUS_PERCENT) + '%';
|
||||
}
|
||||
|
||||
get yP(): string {
|
||||
return (this.y - JUNCTION_RADIUS_PERCENT) + '%';
|
||||
}
|
||||
|
||||
get wP(): string {
|
||||
return (JUNCTION_RADIUS_PERCENT * 2) + '%';
|
||||
}
|
||||
|
||||
get hP(): string {
|
||||
return (JUNCTION_RADIUS_PERCENT * 2) + '%';
|
||||
}
|
||||
|
||||
}
|
||||
26
src/main/angular/src/app/editor/parseColor.ts
Normal file
26
src/main/angular/src/app/editor/parseColor.ts
Normal file
@ -0,0 +1,26 @@
|
||||
export function parseColor(color: string): { r: number; g: number; b: number } {
|
||||
const ctx = document.createElement("canvas").getContext("2d");
|
||||
if (!ctx) {
|
||||
throw new Error();
|
||||
}
|
||||
ctx.fillStyle = color;
|
||||
|
||||
const computed = ctx.fillStyle;
|
||||
if (!computed.startsWith("#")) {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
let hex = computed.slice(1);
|
||||
if (hex.length === 3) {
|
||||
hex = hex.split("").map(c => c + c).join("");
|
||||
}
|
||||
if (hex.length !== 6) {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
const r = parseInt(hex.substring(0, 2), 16);
|
||||
const g = parseInt(hex.substring(2, 4), 16);
|
||||
const b = parseInt(hex.substring(4, 6), 16);
|
||||
|
||||
return {r, g, b};
|
||||
}
|
||||
59
src/main/angular/src/app/editor/parts/Part.ts
Normal file
59
src/main/angular/src/app/editor/parts/Part.ts
Normal file
@ -0,0 +1,59 @@
|
||||
import {Junction} from '../junction/Junction';
|
||||
import {PartType} from './PartType';
|
||||
|
||||
export const RASTER = 50;
|
||||
|
||||
export abstract class Part {
|
||||
|
||||
protected constructor(
|
||||
readonly x: number,
|
||||
readonly y: number,
|
||||
readonly type: PartType,
|
||||
readonly junctions: Junction[],
|
||||
readonly w: number = 3,
|
||||
readonly h: number = 3,
|
||||
) {
|
||||
//
|
||||
}
|
||||
|
||||
get xR(): number {
|
||||
return this.x * RASTER;
|
||||
}
|
||||
|
||||
get yR(): number {
|
||||
return this.y * RASTER;
|
||||
}
|
||||
|
||||
get wR(): number {
|
||||
return this.w * RASTER;
|
||||
}
|
||||
|
||||
get hR(): number {
|
||||
return this.h * RASTER;
|
||||
}
|
||||
|
||||
get xP(): string {
|
||||
return this.xR + 'px';
|
||||
}
|
||||
|
||||
get yP(): string {
|
||||
return this.yR + 'px';
|
||||
}
|
||||
|
||||
get wP(): string {
|
||||
return this.wR + 'px';
|
||||
}
|
||||
|
||||
get hP(): string {
|
||||
return this.hR + 'px';
|
||||
}
|
||||
|
||||
ngClass(): string[] {
|
||||
return ['part', 'part' + this.type, ...this.ngClass2()];
|
||||
}
|
||||
|
||||
protected ngClass2(): string[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
}
|
||||
4
src/main/angular/src/app/editor/parts/PartType.ts
Normal file
4
src/main/angular/src/app/editor/parts/PartType.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export enum PartType {
|
||||
Battery = 'Battery',
|
||||
Light = 'Light',
|
||||
}
|
||||
33
src/main/angular/src/app/editor/parts/battery/Battery.ts
Normal file
33
src/main/angular/src/app/editor/parts/battery/Battery.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import {Part} from "../Part";
|
||||
import {Junction} from '../../junction/Junction';
|
||||
import {PartType} from '../PartType';
|
||||
import {siPrefix} from '../../siPrefix';
|
||||
|
||||
export class Battery extends Part {
|
||||
|
||||
constructor(
|
||||
x: number,
|
||||
y: number,
|
||||
readonly voltage: number,
|
||||
public current: number,
|
||||
) {
|
||||
super(
|
||||
x,
|
||||
y,
|
||||
PartType.Battery,
|
||||
[
|
||||
new Junction(15, 50, "-"),
|
||||
new Junction(85, 50, "+"),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
get voltageStr(): string {
|
||||
return siPrefix(this.voltage, 'V', 2);
|
||||
}
|
||||
|
||||
get currentStr(): string {
|
||||
return siPrefix(this.current, 'A', 2);
|
||||
}
|
||||
|
||||
}
|
||||
46
src/main/angular/src/app/editor/parts/light/Light.ts
Normal file
46
src/main/angular/src/app/editor/parts/light/Light.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import {Part} from "../Part";
|
||||
import {Junction} from '../../junction/Junction';
|
||||
import {PartType} from '../PartType';
|
||||
import {siPrefix} from '../../siPrefix';
|
||||
import {fadeGrayToYellow} from '../../fadeGrayToYellow';
|
||||
|
||||
export class Light extends Part {
|
||||
|
||||
constructor(
|
||||
x: number,
|
||||
y: number,
|
||||
readonly voltageMax: number,
|
||||
readonly voltage: number,
|
||||
public current: number,
|
||||
) {
|
||||
super(
|
||||
x,
|
||||
y,
|
||||
PartType.Light,
|
||||
[
|
||||
new Junction(15, 50, "a"),
|
||||
new Junction(85, 50, "b"),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
get defect(): boolean {
|
||||
return this.voltage > this.voltageMax;
|
||||
}
|
||||
|
||||
get voltageStr(): string {
|
||||
return siPrefix(this.voltage, 'V', 2);
|
||||
}
|
||||
|
||||
get currentStr(): string {
|
||||
return siPrefix(this.current, 'A', 2);
|
||||
}
|
||||
|
||||
fill(): string {
|
||||
if (this.defect) {
|
||||
return "#6e4122"
|
||||
}
|
||||
return fadeGrayToYellow(this.voltage / this.voltageMax, "#888", "#FF0");
|
||||
}
|
||||
|
||||
}
|
||||
15
src/main/angular/src/app/editor/siPrefix.ts
Normal file
15
src/main/angular/src/app/editor/siPrefix.ts
Normal file
@ -0,0 +1,15 @@
|
||||
export const SI_PREFIXES: string[] = ['f', 'p', 'n', 'µ', 'm', '', 'k', 'M', 'G', 'T', 'E', 'P'];
|
||||
|
||||
export function siPrefix(value: number, unit: string, minDigits: number): string {
|
||||
const exp0 = Math.log10(value);
|
||||
const group = Math.floor(exp0 / 3);
|
||||
const index = group + 5;
|
||||
const prefix = SI_PREFIXES[index];
|
||||
const exp1 = group * 3;
|
||||
const factor = Math.pow(10, -exp1);
|
||||
const newValue = factor * value;
|
||||
const hasDigits = Math.floor(Math.log10(newValue)) + 1;
|
||||
const decimals = Math.max(0, minDigits - hasDigits);
|
||||
const newValueStr2 = newValue.toFixed(decimals);
|
||||
return `${newValueStr2}${prefix}${unit}`;
|
||||
}
|
||||
14
src/main/angular/src/index.html
Normal file
14
src/main/angular/src/index.html
Normal file
@ -0,0 +1,14 @@
|
||||
<!doctype html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Elektro</title>
|
||||
<base href="/">
|
||||
<meta content="width=device-width, initial-scale=1" name="viewport">
|
||||
<!--suppress HtmlUnknownTarget -->
|
||||
<link rel="icon" href="favicon.svg">
|
||||
</head>
|
||||
<body>
|
||||
<app-root></app-root>
|
||||
</body>
|
||||
</html>
|
||||
6
src/main/angular/src/main.ts
Normal file
6
src/main/angular/src/main.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import {bootstrapApplication} from '@angular/platform-browser';
|
||||
import {appConfig} from './app/app.config';
|
||||
import {AppComponent} from './app/app.component';
|
||||
|
||||
bootstrapApplication(AppComponent, appConfig)
|
||||
.catch((err) => console.error(err));
|
||||
10
src/main/angular/src/styles.less
Normal file
10
src/main/angular/src/styles.less
Normal file
@ -0,0 +1,10 @@
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: sans-serif;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
15
src/main/angular/tsconfig.app.json
Normal file
15
src/main/angular/tsconfig.app.json
Normal file
@ -0,0 +1,15 @@
|
||||
/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
|
||||
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./out-tsc/app",
|
||||
"types": []
|
||||
},
|
||||
"files": [
|
||||
"src/main.ts"
|
||||
],
|
||||
"include": [
|
||||
"src/**/*.d.ts"
|
||||
]
|
||||
}
|
||||
27
src/main/angular/tsconfig.json
Normal file
27
src/main/angular/tsconfig.json
Normal file
@ -0,0 +1,27 @@
|
||||
/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
|
||||
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
|
||||
{
|
||||
"compileOnSave": false,
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist/out-tsc",
|
||||
"strict": true,
|
||||
"noImplicitOverride": true,
|
||||
"noPropertyAccessFromIndexSignature": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"skipLibCheck": true,
|
||||
"isolatedModules": true,
|
||||
"esModuleInterop": true,
|
||||
"experimentalDecorators": true,
|
||||
"moduleResolution": "bundler",
|
||||
"importHelpers": true,
|
||||
"target": "ES2022",
|
||||
"module": "ES2022"
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"enableI18nLegacyMessageIdFormat": false,
|
||||
"strictInjectionParameters": true,
|
||||
"strictInputAccessModifiers": true,
|
||||
"strictTemplates": true
|
||||
}
|
||||
}
|
||||
15
src/main/angular/tsconfig.spec.json
Normal file
15
src/main/angular/tsconfig.spec.json
Normal file
@ -0,0 +1,15 @@
|
||||
/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
|
||||
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./out-tsc/spec",
|
||||
"types": [
|
||||
"jasmine"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.d.ts"
|
||||
]
|
||||
}
|
||||
9
src/main/java/de/ph87/data/Main.java
Normal file
9
src/main/java/de/ph87/data/Main.java
Normal file
@ -0,0 +1,9 @@
|
||||
package de.ph87.data;
|
||||
|
||||
public class Main {
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("Hello, World!");
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user