Skip to content

Commit cd80d8d

Browse files
authored
Merge pull request #187 from UnstableDesign/165-image-map-more-colors-than-file-contains
Added Color Space Editing for Uploaded Images
2 parents 18fc23b + cfffa4d commit cd80d8d

33 files changed

+1242
-459
lines changed

src/app/app.component.ts

Lines changed: 65 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { LoadfileComponent } from './core/modal/loadfile/loadfile.component';
99
import { LoginComponent } from './core/modal/login/login.component';
1010
import { MaterialModal } from './core/modal/material/material.modal';
1111
import { createCell } from './core/model/cell';
12-
import { Draft, DraftNode, DraftNodeProxy, FileObj, LoadResponse, Loom, LoomSettings, NodeComponentProxy, SaveObj, TreeNode, TreeNodeProxy } from './core/model/datatypes';
12+
import { Draft, DraftNode, DraftNodeProxy, FileObj, IndexedColorImageInstance, IndexedColorMediaProxy, LoadResponse, Loom, LoomSettings, NodeComponentProxy, SaveObj, TreeNode, TreeNodeProxy } from './core/model/datatypes';
1313
import { defaults, density_units, editor_modes, loom_types, origin_option_list } from './core/model/defaults';
1414
import { copyDraft, getDraftName, initDraftWithParams } from './core/model/drafts';
1515
import { copyLoom, copyLoomSettings, getLoomUtilByType } from './core/model/looms';
@@ -18,7 +18,7 @@ import { AuthService } from './core/provider/auth.service';
1818
import { DesignmodesService } from './core/provider/designmodes.service';
1919
import { FileService } from './core/provider/file.service';
2020
import { FilesystemService } from './core/provider/filesystem.service';
21-
import { ImageService } from './core/provider/image.service';
21+
import { MediaService } from './core/provider/media.service';
2222
import { MaterialsService } from './core/provider/materials.service';
2323
import { NotesService } from './core/provider/notes.service';
2424
import { OperationService } from './core/provider/operation.service';
@@ -105,7 +105,7 @@ export class AppComponent implements OnInit{
105105
public files: FilesystemService,
106106
private fs: FileService,
107107
private http: HttpClient,
108-
private image: ImageService,
108+
private media: MediaService,
109109
private ms: MaterialsService,
110110
public multiselect: MultiselectService,
111111
private ops: OperationService,
@@ -194,6 +194,7 @@ export class AppComponent implements OnInit{
194194
this.vs.clearPin();
195195
this.vs.clearViewer();
196196
this.mixer.clearView();
197+
this.media.clearMedia();
197198
this.editor.clearAll();
198199
this.viewer.clearView();
199200
this.tree.clear();
@@ -551,8 +552,38 @@ export class AppComponent implements OnInit{
551552

552553

553554
insertPasteFile(result: LoadResponse){
554-
console.log("INSERTING ", result)
555555
this.processFileData(result.data).then(data => {
556+
557+
//after we have processed the data, we need to now relink any images that were duplicated in the process.
558+
let image_id_map = [];
559+
result.data.indexed_image_data.forEach(image => {
560+
let media_item: IndexedColorImageInstance = this.media.duplicateIndexedColorImageInstance(image.id);
561+
image_id_map.push({from: image.id, to: media_item.id})
562+
})
563+
564+
565+
result.data.ops.forEach(op => {
566+
567+
let op_base = this.ops.getOp(op.name);
568+
op_base.params.forEach((param,ndx) => {
569+
570+
if(param.type == 'file'){
571+
572+
let from = op.params[ndx];
573+
let entry = image_id_map.find(el => el.from == from);
574+
575+
if(entry !== undefined){
576+
let img_instance = <IndexedColorImageInstance> this.media.getMedia(entry.to);
577+
//this is just setting it locally, it needs to set the actual operation
578+
let op_node = this.tree.getOpNode(op.node_id);
579+
op_node.params[ndx] = {id: entry.to, data:img_instance.img};
580+
581+
}
582+
}
583+
})
584+
})
585+
586+
556587
this.saveFile();
557588
}
558589
).catch(console.error);
@@ -670,7 +701,6 @@ export class AppComponent implements OnInit{
670701
loadNewFile(result: LoadResponse, source: string) : Promise<any>{
671702

672703

673-
console.log("LOAD RESPONSE ", result)
674704

675705
return this.files.pushToLoadedFilesAndFocus(result.id, result.name, result.desc)
676706
.then(res => {
@@ -921,6 +951,7 @@ onPasteSelections(){
921951
this.upload_modal.afterClosed().subscribe(loadResponse => {
922952
if(loadResponse !== undefined && loadResponse != true)
923953
this.loadNewFile(loadResponse, 'openFile');
954+
this.upload_modal = undefined;
924955

925956
});
926957
}
@@ -963,21 +994,39 @@ async processFileData(data: FileObj) : Promise<string|void>{
963994

964995
//1. filter any operations with a parameter of type file, and load the associated file.
965996
const images_to_load = [];
997+
998+
if(data.filename !== 'paste'){
999+
//only load in new files if this is a true load event, if it is pasting from exisitng files, it doesn't need to re-analyze the images.
1000+
if(utilInstance.sameOrNewerVersion(data.version, '4.1.7')){
1001+
//LOAD THE NEW FILE OBJECT
1002+
data.indexed_image_data.forEach(el => {
1003+
images_to_load.push({id: el.id, ref: el.ref, data:{colors: el.colors, color_mapping: el.color_mapping}});
1004+
})
1005+
1006+
}else{
1007+
console.log("LOADING OLDER VERSION ")
1008+
data.ops.forEach(op => {
1009+
const internal_op = this.ops.getOp(op.name);
1010+
if(internal_op === undefined || internal_op == null|| internal_op.params === undefined) return;
1011+
const param_types = internal_op.params.map(el => el.type);
1012+
param_types.forEach((p, ndx) => {
1013+
//older version stored the media object reference in the parameter
1014+
if(p == 'file'){
1015+
let new_id = utilInstance.generateId(8);
1016+
images_to_load.push({id: new_id, ref: op.params[ndx], data:null});
1017+
op.params[ndx] = new_id; //convert the value stored in memory to the instance id.
1018+
}
1019+
});
1020+
})
1021+
1022+
}
1023+
}
9661024

967-
data.ops.forEach(op => {
968-
const internal_op = this.ops.getOp(op.name);
969-
if(internal_op === undefined || internal_op == null|| internal_op.params === undefined) return;
970-
const param_types = internal_op.params.map(el => el.type);
971-
param_types.forEach((p, ndx) => {
972-
if(p === 'file'){
973-
images_to_load.push(op.params[ndx]);
974-
}
975-
});
976-
})
9771025

9781026

9791027

980-
return this.image.loadFiles(images_to_load).then(el => {
1028+
1029+
return this.media.loadMedia(images_to_load).then(el => {
9811030
//2. check the op names, if any op names are old, relink the newer version of that operation. If not match is found, replaces with Rect.
9821031
return this.tree.replaceOutdatedOps(data.ops);
9831032
})

src/app/core/core.module.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ import { SelectionComponent } from './ui/draft-rendering/selection/selection.com
5454
import { ViewerService } from './provider/viewer.service';
5555
import { ViewadjustService } from './provider/viewadjust.service';
5656
import { ViewadjustComponent } from './viewadjust/viewadjust.component';
57+
import { ImageeditorComponent } from './modal/imageeditor/imageeditor.component';
58+
import { MediaService } from './provider/media.service';
59+
import { OperationService } from './provider/operation.service';
5760

5861

5962

@@ -105,6 +108,7 @@ import { ViewadjustComponent } from './viewadjust/viewadjust.component';
105108
ExamplesComponent,
106109
LoadfileComponent,
107110
FilebrowserComponent,
111+
ImageeditorComponent,
108112
EventsDirective,
109113
WelcomeComponent,
110114
SelectionComponent,
@@ -118,7 +122,9 @@ import { ViewadjustComponent } from './viewadjust/viewadjust.component';
118122
AuthService,
119123
RenderService,
120124
ViewerService,
121-
ViewadjustService
125+
ViewadjustService,
126+
MediaService,
127+
OperationService
122128
],
123129
exports: [
124130
CommonModule,
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
<p mat-dialog-title
2+
cdkDrag
3+
cdkDragRootElement=".cdk-overlay-pane"
4+
cdkDragHandle>
5+
<span *ngIf="editable">View/Edit Image Colors</span>
6+
<span *ngIf="!editable">View Image Colors</span>
7+
</p>
8+
9+
10+
<mat-dialog-content >
11+
12+
<div class="content_left">
13+
<p *ngIf="editable">Use this editor to modify the color space of your image. You can do this grouping colors together that you would like the software to treat as the same.</p>
14+
15+
<div class="image_preview" id="image_preview">
16+
<canvas id="preview_canvas"></canvas>
17+
</div>
18+
<div class="dims">{{img.width}} wide x {{img.height}} tall</div>
19+
<div class="dims">filename: {{img.name}}</div>
20+
<div class="dims">media id: {{media_id}}</div>
21+
22+
</div>
23+
<div class="content_right">
24+
25+
<div class="data">
26+
27+
<div class="mappings">
28+
29+
30+
<div class="header">
31+
<span>original color space ({{img.colors.length}})</span>
32+
</div>
33+
34+
35+
<div *ngFor="let color of color_table; index as i" class="data_row">
36+
37+
<!--original swatch-->
38+
<div class="swatch" [style.background-color]="color.from_hex">
39+
<div class="centered_id">{{color.from}}</div>
40+
</div>
41+
42+
<!--color mapped to-->
43+
<mat-form-field>
44+
<mat-label>groups with:</mat-label>
45+
<mat-select [disabled]="!editable" [(value)]="color.to" (selectionChange)="mappingChanged(color.from, $event)">
46+
<mat-option *ngFor="let to_color of img.colors; index as i" [value]="i">
47+
<div class="swatch" [style.background-color]="to_color.hex">
48+
<div class="centered_id">{{i}}</div>
49+
</div>
50+
</mat-option>
51+
</mat-select>
52+
</mat-form-field>
53+
54+
<!--color mapped to-->
55+
56+
<!-- <div class="swatch" [style.background-color]="color.to_hex">
57+
{{color.to}}
58+
</div> -->
59+
60+
</div>
61+
</div>
62+
<div class="results">
63+
64+
<div class="header">
65+
<span>resulting color space ({{resulting_color_space.length}})</span>
66+
</div>
67+
<!--resulting swatch-->
68+
<div *ngFor="let color of resulting_color_space ; index as i" class="swatch" [style.background-color]="color.to_hex">
69+
<div class="centered_id"> {{color.to}}</div>
70+
</div>
71+
72+
</div>
73+
</div>
74+
</div>
75+
76+
77+
</mat-dialog-content>
78+
<mat-dialog-actions align="end">
79+
<button mat-button mat-dialog-close>Save and Close</button>
80+
</mat-dialog-actions>
81+
82+
83+
84+
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
.mat-mdc-dialog-content{
2+
display: flex;
3+
flex-direction: row;
4+
gap: 8px
5+
}
6+
7+
.image_preview{
8+
border: thin solid black;
9+
overflow: scroll;
10+
}
11+
12+
13+
.content_left{
14+
display: flex;
15+
flex-direction: column;
16+
width: 66%;
17+
height: 100%;
18+
margin-top: 12px;
19+
}
20+
21+
.content_right{
22+
display: flex;
23+
flex-direction: column;
24+
width: 30%;
25+
}
26+
27+
.header{
28+
display: flex;
29+
flex-direction: row;
30+
gap: 16px;
31+
font-size: 12px;
32+
line-height: 14px;
33+
justify-content: space-between;
34+
padding: 8px 0px;
35+
36+
span{
37+
display: inline-block;
38+
width: 100px;
39+
}
40+
41+
}
42+
43+
.swatch{
44+
display: flex;
45+
width: 40px;
46+
height: 55px;
47+
border: thin solid black;
48+
align-items: center;
49+
justify-content: center;
50+
51+
52+
}
53+
54+
.data{
55+
display: flex;
56+
flex-direction: row;
57+
gap: 16px;
58+
}
59+
60+
.results{
61+
display: flex;
62+
flex-direction: row;
63+
width: 40%;
64+
flex-wrap: wrap;
65+
gap: 8px;
66+
height: min-content;
67+
}
68+
69+
70+
.mappings{
71+
display: flex;
72+
width: 60%;
73+
flex-direction: column;
74+
}
75+
.centered_id{
76+
display: flex;
77+
width: 20px;
78+
height: 20px;
79+
background-color: white;
80+
color: black;
81+
font-size: 12px;
82+
text-align: center;
83+
align-items: center;
84+
justify-content: center;
85+
border-radius: 8px;
86+
border: thin solid black;
87+
88+
}
89+
90+
.data_row{
91+
display: flex;
92+
flex-direction: row;
93+
align-items: top;
94+
gap: 8px;
95+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { ComponentFixture, TestBed } from '@angular/core/testing';
2+
3+
import { ImageeditorComponent } from './imageeditor.component';
4+
5+
describe('ImageeditorComponent', () => {
6+
let component: ImageeditorComponent;
7+
let fixture: ComponentFixture<ImageeditorComponent>;
8+
9+
beforeEach(async () => {
10+
await TestBed.configureTestingModule({
11+
imports: [ImageeditorComponent]
12+
})
13+
.compileComponents();
14+
15+
fixture = TestBed.createComponent(ImageeditorComponent);
16+
component = fixture.componentInstance;
17+
fixture.detectChanges();
18+
});
19+
20+
it('should create', () => {
21+
expect(component).toBeTruthy();
22+
});
23+
});

0 commit comments

Comments
 (0)