Add custom aspect ratio for the projector
Change the client to accept aspect ratios like: 4:3, 16:9, 16:10 or custom over a textfield Change server to accept aspect ratios and dropped height
This commit is contained in:
parent
4451fe979e
commit
ff90f9490c
@ -65,7 +65,8 @@ export class Projector extends BaseModel<Projector> {
|
|||||||
public scroll: number;
|
public scroll: number;
|
||||||
public name: string;
|
public name: string;
|
||||||
public width: number;
|
public width: number;
|
||||||
public height: number;
|
public aspect_ratio_numerator: number;
|
||||||
|
public aspect_ratio_denominator: number;
|
||||||
public reference_projector_id: number;
|
public reference_projector_id: number;
|
||||||
public projectiondefaults_id: number[];
|
public projectiondefaults_id: number[];
|
||||||
public color: string;
|
public color: string;
|
||||||
@ -79,6 +80,34 @@ export class Projector extends BaseModel<Projector> {
|
|||||||
public show_title: boolean;
|
public show_title: boolean;
|
||||||
public show_logo: boolean;
|
public show_logo: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns Calculate the height of the projector
|
||||||
|
*/
|
||||||
|
public get height(): number {
|
||||||
|
const ratio = this.aspect_ratio_numerator / this.aspect_ratio_denominator;
|
||||||
|
return this.width / ratio;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the aspect ratio as string
|
||||||
|
*/
|
||||||
|
public get aspectRatio(): string {
|
||||||
|
return [this.aspect_ratio_numerator, this.aspect_ratio_denominator].join(':');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the aspect ratio
|
||||||
|
*/
|
||||||
|
public set aspectRatio(ratioString: string) {
|
||||||
|
const ratio = ratioString.split(':').map(x => +x);
|
||||||
|
if (ratio.length === 2) {
|
||||||
|
this.aspect_ratio_numerator = ratio[0];
|
||||||
|
this.aspect_ratio_denominator = ratio[1];
|
||||||
|
} else {
|
||||||
|
throw new Error('Projector received unexpected aspect ratio! ' + ratio.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public constructor(input?: any) {
|
public constructor(input?: any) {
|
||||||
super(Projector.COLLECTIONSTRING, input);
|
super(Projector.COLLECTIONSTRING, input);
|
||||||
}
|
}
|
||||||
|
@ -15,22 +15,40 @@
|
|||||||
|
|
||||||
<h3 translate>Resolution and size</h3>
|
<h3 translate>Resolution and size</h3>
|
||||||
<!-- Aspect ratio field -->
|
<!-- Aspect ratio field -->
|
||||||
<mat-radio-group formControlName="aspectRatio" name="aspectRatio">
|
<div>
|
||||||
<mat-radio-button
|
<mat-radio-group formControlName="aspectRatio" name="aspectRatio">
|
||||||
*ngFor="let ratio of aspectRatiosKeys"
|
<mat-radio-button
|
||||||
[value]="ratio"
|
*ngFor="let ratio of defaultAspectRatio"
|
||||||
(change)="aspectRatioChanged($event)"
|
[value]="ratio"
|
||||||
>
|
(change)="onCustomAspectRatio(false)"
|
||||||
{{ ratio }}
|
>
|
||||||
</mat-radio-button>
|
{{ ratio }}
|
||||||
</mat-radio-group>
|
</mat-radio-button>
|
||||||
|
|
||||||
|
<!-- Custom aspect ratio -->
|
||||||
|
<mat-radio-button (change)="onCustomAspectRatio(true)">
|
||||||
|
{{ 'custom' | translate }}
|
||||||
|
</mat-radio-button>
|
||||||
|
|
||||||
|
<mat-form-field *ngIf="customAspectRatio">
|
||||||
|
<input
|
||||||
|
matInput
|
||||||
|
type="text"
|
||||||
|
formControlName="aspectRatio"
|
||||||
|
[value]="previewProjector.aspectRatio"
|
||||||
|
(change)="setCustomAspectRatio()"
|
||||||
|
placeholder="{{ 'Custom aspect ratio' | translate }}"
|
||||||
|
/>
|
||||||
|
</mat-form-field>
|
||||||
|
</mat-radio-group>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="spacer-top-20 grid-form">
|
<div class="spacer-top-20 grid-form">
|
||||||
<mat-slider
|
<mat-slider
|
||||||
class="grid-start"
|
class="grid-start"
|
||||||
formControlName="width"
|
formControlName="width"
|
||||||
[thumbLabel]="true"
|
[thumbLabel]="true"
|
||||||
[min]="getMinWidth()"
|
[min]="minWidth"
|
||||||
[max]="maxResolution"
|
[max]="maxResolution"
|
||||||
[step]="resolutionChangeStep"
|
[step]="resolutionChangeStep"
|
||||||
[value]="updateForm.value.width"
|
[value]="updateForm.value.width"
|
||||||
@ -41,7 +59,7 @@
|
|||||||
matInput
|
matInput
|
||||||
type="number"
|
type="number"
|
||||||
formControlName="width"
|
formControlName="width"
|
||||||
[min]="getMinWidth()"
|
[min]="minWidth"
|
||||||
[max]="maxResolution"
|
[max]="maxResolution"
|
||||||
[step]="resolutionChangeStep"
|
[step]="resolutionChangeStep"
|
||||||
[value]="updateForm.value.width"
|
[value]="updateForm.value.width"
|
||||||
@ -170,7 +188,7 @@
|
|||||||
</form>
|
</form>
|
||||||
<div>
|
<div>
|
||||||
<h3 translate>Preview</h3>
|
<h3 translate>Preview</h3>
|
||||||
<div>
|
<div class="preview-container">
|
||||||
<os-projector #preview *ngIf="previewProjector" [projector]="previewProjector"></os-projector>
|
<os-projector #preview *ngIf="previewProjector" [projector]="previewProjector"></os-projector>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -25,6 +25,7 @@ form {
|
|||||||
.grid-start {
|
.grid-start {
|
||||||
grid-column-start: 1;
|
grid-column-start: 1;
|
||||||
grid-column-end: 1;
|
grid-column-end: 1;
|
||||||
|
margin: auto 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,6 +46,10 @@ form {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.preview-container {
|
||||||
|
border: 1px solid lightgray;
|
||||||
|
}
|
||||||
|
|
||||||
.no-markup {
|
.no-markup {
|
||||||
/* Do not let the a tag ruin the projector */
|
/* Do not let the a tag ruin the projector */
|
||||||
color: inherit;
|
color: inherit;
|
||||||
|
@ -8,7 +8,7 @@ import {
|
|||||||
ViewEncapsulation
|
ViewEncapsulation
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||||
import { MAT_DIALOG_DATA, MatDialogRef, MatRadioChange, MatSnackBar } from '@angular/material';
|
import { MAT_DIALOG_DATA, MatDialogRef, MatSnackBar } from '@angular/material';
|
||||||
import { Title } from '@angular/platform-browser';
|
import { Title } from '@angular/platform-browser';
|
||||||
|
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
@ -23,18 +23,6 @@ import { ClockSlideService } from '../../services/clock-slide.service';
|
|||||||
import { ViewProjectionDefault } from '../../models/view-projection-default';
|
import { ViewProjectionDefault } from '../../models/view-projection-default';
|
||||||
import { ViewProjector } from '../../models/view-projector';
|
import { ViewProjector } from '../../models/view-projector';
|
||||||
|
|
||||||
/**
|
|
||||||
* All supported aspect rations for projectors.
|
|
||||||
*/
|
|
||||||
const aspectRatios: { [ratio: string]: number } = {
|
|
||||||
'4:3': 4 / 3,
|
|
||||||
'16:9': 16 / 9,
|
|
||||||
'16:10': 16 / 10,
|
|
||||||
'30:9': 30 / 9
|
|
||||||
};
|
|
||||||
|
|
||||||
const aspectRatio_30_9_MinWidth = 1150;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dialog to edit the given projector
|
* Dialog to edit the given projector
|
||||||
* Shows a preview
|
* Shows a preview
|
||||||
@ -54,17 +42,17 @@ export class ProjectorEditDialogComponent extends BaseViewComponent implements O
|
|||||||
@ViewChild('preview', { static: false })
|
@ViewChild('preview', { static: false })
|
||||||
public preview: ProjectorComponent;
|
public preview: ProjectorComponent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* aspect ratios
|
||||||
|
*/
|
||||||
|
public defaultAspectRatio: string[] = ['4:3', '16:9', '16:10'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The update form. Will be refreahed for each projector. Just one update
|
* The update form. Will be refreahed for each projector. Just one update
|
||||||
* form can be shown per time.
|
* form can be shown per time.
|
||||||
*/
|
*/
|
||||||
public updateForm: FormGroup;
|
public updateForm: FormGroup;
|
||||||
|
|
||||||
/**
|
|
||||||
* All aspect ratio keys/strings for the UI.
|
|
||||||
*/
|
|
||||||
public aspectRatiosKeys: string[];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* All ProjectionDefaults to select from.
|
* All ProjectionDefaults to select from.
|
||||||
*/
|
*/
|
||||||
@ -80,11 +68,26 @@ export class ProjectorEditDialogComponent extends BaseViewComponent implements O
|
|||||||
*/
|
*/
|
||||||
public maxResolution = 2000;
|
public maxResolution = 2000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* define the minWidth
|
||||||
|
*/
|
||||||
|
public minWidth = 800;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define the step of resolution changes
|
* Define the step of resolution changes
|
||||||
*/
|
*/
|
||||||
public resolutionChangeStep = 10;
|
public resolutionChangeStep = 10;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine to use custom aspect ratios
|
||||||
|
*/
|
||||||
|
public customAspectRatio: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* regular expression to check for aspect ratio strings
|
||||||
|
*/
|
||||||
|
private aspectRatioRe = RegExp('[1-9]+[0-9]*:[1-9]+[0-9]*');
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
title: Title,
|
title: Title,
|
||||||
translate: TranslateService,
|
translate: TranslateService,
|
||||||
@ -98,15 +101,18 @@ export class ProjectorEditDialogComponent extends BaseViewComponent implements O
|
|||||||
private cd: ChangeDetectorRef
|
private cd: ChangeDetectorRef
|
||||||
) {
|
) {
|
||||||
super(title, translate, matSnackBar);
|
super(title, translate, matSnackBar);
|
||||||
this.aspectRatiosKeys = Object.keys(aspectRatios);
|
|
||||||
|
|
||||||
if (projector) {
|
if (projector) {
|
||||||
this.previewProjector = new Projector(projector.getModel());
|
this.previewProjector = new Projector(projector.getModel());
|
||||||
|
|
||||||
|
if (!this.defaultAspectRatio.some(ratio => ratio === this.previewProjector.aspectRatio)) {
|
||||||
|
this.customAspectRatio = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.updateForm = formBuilder.group({
|
this.updateForm = formBuilder.group({
|
||||||
name: ['', Validators.required],
|
name: ['', Validators.required],
|
||||||
aspectRatio: ['', Validators.required],
|
aspectRatio: ['', [Validators.required, Validators.pattern(this.aspectRatioRe)]],
|
||||||
width: [0, Validators.required],
|
width: [0, Validators.required],
|
||||||
projectiondefaults_id: [[]],
|
projectiondefaults_id: [[]],
|
||||||
clock: [true],
|
clock: [true],
|
||||||
@ -143,7 +149,6 @@ export class ProjectorEditDialogComponent extends BaseViewComponent implements O
|
|||||||
this.updateForm.patchValue(this.projector.projector);
|
this.updateForm.patchValue(this.projector.projector);
|
||||||
this.updateForm.patchValue({
|
this.updateForm.patchValue({
|
||||||
name: this.translate.instant(this.projector.name),
|
name: this.translate.instant(this.projector.name),
|
||||||
aspectRatio: this.getAspectRatioKey(),
|
|
||||||
clock: this.clockSlideService.isProjectedOn(this.projector)
|
clock: this.clockSlideService.isProjectedOn(this.projector)
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -174,8 +179,8 @@ export class ProjectorEditDialogComponent extends BaseViewComponent implements O
|
|||||||
* Saves the current changes on the projector
|
* Saves the current changes on the projector
|
||||||
*/
|
*/
|
||||||
public async applyChanges(): Promise<void> {
|
public async applyChanges(): Promise<void> {
|
||||||
const updateProjector: Partial<Projector> = this.updateForm.value;
|
const updateProjector: Projector = new Projector();
|
||||||
updateProjector.height = this.calcHeight(this.updateForm.value.width, this.updateForm.value.aspectRatio);
|
Object.assign(updateProjector, this.updateForm.value);
|
||||||
try {
|
try {
|
||||||
await this.clockSlideService.setProjectedOn(this.projector, this.updateForm.value.clock);
|
await this.clockSlideService.setProjectedOn(this.projector, this.updateForm.value.clock);
|
||||||
await this.repo.update(updateProjector, this.projector);
|
await this.repo.update(updateProjector, this.projector);
|
||||||
@ -189,26 +194,13 @@ export class ProjectorEditDialogComponent extends BaseViewComponent implements O
|
|||||||
* @param previewUpdate
|
* @param previewUpdate
|
||||||
*/
|
*/
|
||||||
public onChangeForm(): void {
|
public onChangeForm(): void {
|
||||||
if (this.previewProjector && this.projector) {
|
if (this.previewProjector && this.projector && this.updateForm.valid) {
|
||||||
Object.assign(this.previewProjector, this.updateForm.value);
|
Object.assign(this.previewProjector, this.updateForm.value);
|
||||||
this.previewProjector.height = this.calcHeight(
|
|
||||||
this.updateForm.value.width,
|
|
||||||
this.updateForm.value.aspectRatio
|
|
||||||
);
|
|
||||||
this.preview.setProjector(this.previewProjector);
|
this.preview.setProjector(this.previewProjector);
|
||||||
this.cd.markForCheck();
|
this.cd.markForCheck();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper to calc height
|
|
||||||
* @param width
|
|
||||||
* @param aspectRatio
|
|
||||||
*/
|
|
||||||
private calcHeight(width: number, aspectRatio: string): number {
|
|
||||||
return Math.round(width / aspectRatios[aspectRatio]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resets the given form field to the given default.
|
* Resets the given form field to the given default.
|
||||||
*/
|
*/
|
||||||
@ -218,41 +210,23 @@ export class ProjectorEditDialogComponent extends BaseViewComponent implements O
|
|||||||
this.updateForm.patchValue(patchValue);
|
this.updateForm.patchValue(patchValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
public aspectRatioChanged(event: MatRadioChange): void {
|
/**
|
||||||
let width: number;
|
* Sets the aspect Ratio to custom
|
||||||
if (event.value === '30:9' && this.updateForm.value.width < aspectRatio_30_9_MinWidth) {
|
* @param event
|
||||||
width = aspectRatio_30_9_MinWidth;
|
*/
|
||||||
} else {
|
public onCustomAspectRatio(event: boolean): void {
|
||||||
width = this.updateForm.value.width;
|
this.customAspectRatio = event;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates the aspect ratio of the given projector.
|
* Sets and validates custom aspect ratio values
|
||||||
* If no matching ratio is found, the first ratio is returned.
|
|
||||||
*
|
|
||||||
* @param projector The projector to check
|
|
||||||
* @returns the found ratio key.
|
|
||||||
*/
|
*/
|
||||||
public getAspectRatioKey(): string {
|
public setCustomAspectRatio(): void {
|
||||||
const ratio = this.projector.width / this.projector.height;
|
const formRatio = this.updateForm.get('aspectRatio').value;
|
||||||
const RATIO_ENVIRONMENT = 0.05;
|
const validatedRatio = formRatio.match(this.aspectRatioRe);
|
||||||
const foundRatioKey = Object.keys(aspectRatios).find(key => {
|
if (validatedRatio && validatedRatio[0]) {
|
||||||
const value = aspectRatios[key];
|
const ratio = validatedRatio[0];
|
||||||
return value >= ratio - RATIO_ENVIRONMENT && value <= ratio + RATIO_ENVIRONMENT;
|
this.updateForm.get('aspectRatio').setValue(ratio);
|
||||||
});
|
|
||||||
if (!foundRatioKey) {
|
|
||||||
return Object.keys(aspectRatios)[0];
|
|
||||||
} else {
|
|
||||||
return foundRatioKey;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public getMinWidth(): number {
|
|
||||||
if (this.updateForm.value.aspectRatio === '30:9') {
|
|
||||||
return aspectRatio_30_9_MinWidth;
|
|
||||||
} else {
|
|
||||||
return 800;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
23
openslides/core/migrations/0026_projector_size_1.py
Normal file
23
openslides/core/migrations/0026_projector_size_1.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# Generated by Django 2.2.6 on 2019-11-22 11:24
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("core", "0025_projector_color"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="projector",
|
||||||
|
name="aspect_ratio_numerator",
|
||||||
|
field=models.PositiveIntegerField(default=16),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="projector",
|
||||||
|
name="aspect_ratio_denominator",
|
||||||
|
field=models.PositiveIntegerField(default=9),
|
||||||
|
),
|
||||||
|
]
|
45
openslides/core/migrations/0027_projector_size_2.py
Normal file
45
openslides/core/migrations/0027_projector_size_2.py
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
# Generated by Finn Stutzenstein on 2019-11-22 11:42
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
def calculate_aspect_ratios(apps, schema_editor):
|
||||||
|
"""
|
||||||
|
Assignes every projector one aspect ratio of the ones, that OS
|
||||||
|
supported until this migration. If no matching ratio was found, the
|
||||||
|
default of 16:9 is assigned.
|
||||||
|
"""
|
||||||
|
Projector = apps.get_model("core", "Projector")
|
||||||
|
ratio_environment = 0.05
|
||||||
|
aspect_ratios = {
|
||||||
|
4 / 3: (4, 3),
|
||||||
|
16 / 9: (16, 9),
|
||||||
|
16 / 10: (16, 10),
|
||||||
|
30 / 9: (30, 9),
|
||||||
|
}
|
||||||
|
|
||||||
|
for projector in Projector.objects.all():
|
||||||
|
projector_ratio = projector.width / projector.height
|
||||||
|
ratio = (16, 9) # default, if no matching aspect ratio was found.
|
||||||
|
# Search ratio, that fits to the projector_ratio. Take first one found.
|
||||||
|
for value, _ratio in aspect_ratios.items():
|
||||||
|
if (
|
||||||
|
value >= projector_ratio - ratio_environment
|
||||||
|
and value <= projector_ratio + ratio_environment
|
||||||
|
):
|
||||||
|
ratio = _ratio
|
||||||
|
break
|
||||||
|
projector.aspect_ratio_numerator = ratio[0]
|
||||||
|
projector.aspect_ratio_denominator = ratio[1]
|
||||||
|
projector.save(skip_autoupdate=True)
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("core", "0026_projector_size_1"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RunPython(calculate_aspect_ratios),
|
||||||
|
]
|
14
openslides/core/migrations/0028_projector_size_3.py
Normal file
14
openslides/core/migrations/0028_projector_size_3.py
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# Generated by Finn Stutzenstein on 2019-11-22 12:04
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("core", "0027_projector_size_2"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(model_name="projector", name="height",),
|
||||||
|
]
|
@ -75,7 +75,8 @@ class Projector(RESTModelMixin, models.Model):
|
|||||||
scroll = models.IntegerField(default=0)
|
scroll = models.IntegerField(default=0)
|
||||||
|
|
||||||
width = models.PositiveIntegerField(default=1024)
|
width = models.PositiveIntegerField(default=1024)
|
||||||
height = models.PositiveIntegerField(default=768)
|
aspect_ratio_numerator = models.PositiveIntegerField(default=16)
|
||||||
|
aspect_ratio_denominator = models.PositiveIntegerField(default=9)
|
||||||
|
|
||||||
color = models.CharField(max_length=7, default="#000000")
|
color = models.CharField(max_length=7, default="#000000")
|
||||||
background_color = models.CharField(max_length=7, default="#ffffff")
|
background_color = models.CharField(max_length=7, default="#ffffff")
|
||||||
|
@ -88,7 +88,8 @@ class ProjectorSerializer(ModelSerializer):
|
|||||||
elements_history = JSONSerializerField(read_only=True)
|
elements_history = JSONSerializerField(read_only=True)
|
||||||
|
|
||||||
width = IntegerField(min_value=800, max_value=3840, required=False)
|
width = IntegerField(min_value=800, max_value=3840, required=False)
|
||||||
height = IntegerField(min_value=340, max_value=2880, required=False)
|
aspect_ratio_numerator = IntegerField(min_value=1, required=False)
|
||||||
|
aspect_ratio_denominator = IntegerField(min_value=1, required=False)
|
||||||
|
|
||||||
projectiondefaults = IdPrimaryKeyRelatedField(
|
projectiondefaults = IdPrimaryKeyRelatedField(
|
||||||
many=True, required=False, queryset=ProjectionDefault.objects.all()
|
many=True, required=False, queryset=ProjectionDefault.objects.all()
|
||||||
@ -105,7 +106,8 @@ class ProjectorSerializer(ModelSerializer):
|
|||||||
"scroll",
|
"scroll",
|
||||||
"name",
|
"name",
|
||||||
"width",
|
"width",
|
||||||
"height",
|
"aspect_ratio_numerator",
|
||||||
|
"aspect_ratio_denominator",
|
||||||
"reference_projector",
|
"reference_projector",
|
||||||
"projectiondefaults",
|
"projectiondefaults",
|
||||||
"color",
|
"color",
|
||||||
|
Loading…
Reference in New Issue
Block a user