-
Notifications
You must be signed in to change notification settings - Fork 31
/
ShipBuilder TODO.txt
2947 lines (2819 loc) · 115 KB
/
ShipBuilder TODO.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
================================================================================================================================================================================
- Gist:
- Code organization:
- ShipBuilderLib project, slave of FloatingSandbox project
- ShipBuilder project, top-level project for standalone SB UI
- For testing ShipBuilder alone
- Just MainApp basically
- All in separate ShipBuilder namespace
- Own frame for UI
- Model, View, Controller architecture
- Reuses existing ResourceHelper, only need (at this moment) for separate resource directory and method is Shaders ("Game" and "ShipBuilder")
- On the "structure: pixels vs triangles", Wreno says fine to edit in pixel mode, and allow user to eventually _view_ also in triangle mode (but not necessarily edit)
- On the size: Wreno says "no fixed sized, allow whole world"
- Architecture:
- MainFrame: IUserInterface
|------------------------------>View
|-------> Controller/Tools
|---->ModelController
|---->Model
|------------------------>View
- MainFrame: owns toolboxes, etc.
- Owns Controller and View
- Very thin, calls into Controller for each high-level interaction (e.g. SelectTool(.), OnWorkbenchStateChanged(.))
- Implements IUserInterface with interface required by controller, e.g. UI state changes, mouse capture, undo stack visualization
- Owns WorkbenchState
- Candidate for saved settings
- Contains foreground and background materials, and settings for all tools
- Implements ship load/save logic, giving/getting whole ShipDefinition to/from ModelController
- Controller
- Owns ModelController, takes reference to View
- Main Frame calls into Controller for each user interaction, including button clicks
- Controller->Main Frame callbacks via IUserInterface
- Maintains UI state (e.g. grid toggle, visible layers), instructing View
- Maintains Undo stack (not individual entries), and orchestrates undo stack visualization with IUserInterface
- Maintains interaction state, implemented via Tools
- Owns SelectionManager pseudo-tool
- Owns ClipboardManager pseudo-tool
- ModelController
- Owns Model, takes reference to View
- Implements all interactions and algorithms (e.g. pencil, line, flood, paste, etc.), uploading resulting changes from Model into View,
and creating Undo stack entries
- Model
- All data, (almost) no operations (anemic), fully exported
- Modified by ModelController
- Knows nothing about view
- IsDirty tracking
- View
- Rendering of ship, all layers
- Rendering of tool interactions (e.g. marching ants, squares, etc.)
- Fully OpenGL
- Tools
- "Extensions" of Controller
- Implement state machines for interactions, including visual notifications (marching ants, paste mask, etc.)
- Take references to WorkbenchState (at tool initialization time)
- Selection tool Take references to SelectionManager (at tool initialization time), so that it can save selection to it
- Receive input state events from Controller, and notifications of WorkbenchState changed
- Take references to View and ModelController
- Modify Model via ModelController
- Instruct View for tool interactions, e.g. tool visualizations (lines, paste mask, etc.)
- Have also reference to IUserInterface, e.g. to capture/release mouse
----------------------------------------------------------------------------------------------------------
- Workflows:
- Create New Ship (@menu, @file panel)
- GameController::CreateNew()
- Create New Ship From TExture (@menu, @file panel)
- GameController::CreateNewFromTexture()
- Load Ship (@menu, @file panel)
- GameController::Load()
- Dirtyness:
- Storage is at Model
- Include metadata
- Manipulated by Controller via ModelController methods
- So Controller notifies IUserInterface
- UI Controls:
+ Button to save and go back to game (!StandaloneMode)
+ Button to quit and go back to game (!StandaloneMode)
+ Button to Save (acts also as SaveAs for first save; menu however has also separate SaveAs)
+ Undo stack
+ Add electrical/texture/ropes layer
+ Button to open ShipMetadata dialog
+ Resize button (canvas resize)
+ StatusBar:
+ Own class
+ Try making it a wxPanel rather than a wxStatusBar
+ Cctor w/UnitsSystem
+ From Preferences
+ Also is notified of every change in ship scale, so that it uses it to go from ship to world
+ Notifications
+ Used
+ Areas:
+ Canvas:
+ WorkSpace size and icon (wxh), w/meters/feet as well
+ GameCore::Conversions.h: MetersToFeet, etc.
+ Coords and icon (x,y), w/meters/feet as well
+ Selection/line/rect/etc. size and icon (wxh), w/meters/feet as well
+ Zoom (%)
+ MainFrame: OnViewModelChanged calls a new ReconciliateUIWithViewModel(), which does:
+ StatusBar: set zoom
+ RecalculateWorkCanvasPanning
+ InteractiveTool (for Material, MeasuringTape, etc.)
+ Icon (store images/bitmaps as members)
+ MeasuringTape
+ Material under cursor (for drop tool)
- Tools:
! Most tools should pan when the (captured) mouse is outside of the canvas
- Via BaseTool::ScrollIntoViewIfNeeded, which simply forwards to IUserInterface::ScrollIntoViewIfNeeded(display logical)
+ Tool:
+ Stores all protected references passed from cctors: IUserInterface, ModelController, View
+ Same abstract methods from game - but all pure
- All tools:
+ Pencil (S, E)
+ Settings: Size
+ Eraser (S, E)
+ Settings: Size
+ Flood (S, E)
+ Sampler tool (*)
+ Icon and cursor
+ Anchor
+ Structural, Electrical, and Ropes
+ Need StatusBar "Current Material" first
+ Tool:
+ LeftMouseDown, RightMouseDown: selection
+ Change WorkbenchState fg or bg
+ Notify MainFrame of WorkbenchState change
+ Test that it refreshes
+ MouseMove: change status bar
! Regardless of mouse being down or up
+ Notify MainFrame of material
+ IUserInterface notification, with name of material
+ Zero-out material at:
+ Tool destructor
+ Test:
+ Structural (sample + select)
+ Electrical (sample + select)
+ Ropes (sample + select)
+ Line
+ Measuring Tape
+ Shift lock
+ Notification
+ SetToolType
+ Invoke from MainFrame's OnToolChange onto StatusBar
+ Icon w/2 images
+ Status bar
+ Start- and End-points being in window
+ @ down: clip
+ @ up: fix bug that shows line staying behind
+ @ move:
+ Clip overlay
+ Clip length
+ StructuralRectangle (*)
! Spec:
! Settings: FillWithFore|FillWithBack|NoFill; Size
+ Impl:
+ WorkbenchState settings
+ MainFrame:
+ Button
+ Settings
+ Line size
+ Dropdown for mode
+ Tool:
+ Do
+ See if mCurrentRectangle still needed
+ View: rename "SelectionOverlay" -> "DashedRectangleOverlay"
+ Rename: "Foreground"/"Background" -> "Primary"/"Secondary"
+ Selection
+ Implements selection interaction and rendering of visual feedback *during the interaction only*
+ Takes SelectionManager ref in its cctor, and populates it upon confirmation of selection made
+ Selection spec (*)
- Ellipses
- Traslate (aka move)
+ Magic Eraser (for texture layer only)
+ Tolerance (0-100): % of distance(this color, start color)/max_distance
+ Anti-aliasing (bool): when on, alpha = distance(this color, start color)/tolerance_distance
+ Other edit operations, not via tools - via menu (and buttons?)
+ Auto-trim (on bounding box)
+ Selection/Copy'n'Paste (*):
! Spec:
- ~LayersRegion: container of (regions of) layers, for each layer type
- Elec contains own region of PanelMetadata
- It is Paste's job to ensure clipboard and target "match"
- i.e. ignores non-existing layers (for consistency with ephemeral)
- SelectionManager: just glorified optional<Rect> member of Controller
- Owned by Controller, created @ cctor
- Has reference to IUserInterface (to notify of selection on/off toggles)
- Reset at:
- Selection tool destroy
- Deselect (ctrl^D/deselect button at Select tool's "Tool Settings" ribbon)
- Controller::~Controller (obviously)
- ClipboardManager: stores opt<LayersRegion>
- Owned by WorkbenchState, created @ cctor
- Owned by WorkbenchState, so we may have clipboard surviving reloads
- Has reference to IUserInterface (to notify of clipboard full/empty toggles)
- Never reset
+ ShipLayers == LayersRegion
+ Assets
+ Selection
+ Copy, Cut, Paste button icons
+ Select tool <TLayer>: populates selection Rect into SelectionManager
+ Icon: Selector.png, ready
+ "Tool Settings" ribbon: // - change label of ribbon to actual tool name
+ "All Layers" checkbox
+ Used @ clipboard population
+ "Deselect" button (/CTRL^D)
+ Talks to (base) tool via dynamic_cast
+ Tool:
+ Cursor
+ Mechanics:
+ Base
+ Corner picking
+ Square constraining
+ SelectionManager notifies IUserInterface @ selection presence toggles; used for: Copy, Cut, Deselect buttons
+ Rearc: see if can stop suspending tool at MouseLeaveWindow
+ First test sequence of calls @:
+ From out, L down then enter: expect: Ldown->Mmove; actual: Mmove (so subsequent Lup is unmatched)
+ From in, L down then leave (up happening out): expect: Lup->LeftCanvas; actual: UncapturedMouseOut->Lup
+ No more suspend @ mouse left window (& thus no more resume @ mouse enter window)
+ But new OnLeftCanvas(), treated as a MouseMove() outside of canvas
+ Note: *could* be engaged, in which case this MouseLeave is to be treated as Lup (with commit) followed by LeftCanvas
+ Add above to comment
+ Verify return comes with a MouseMove; if not, also need OnEnter()
! Technically, state machine transitions are:
! Leave == In->Out move
! Move (evt. after Enter):
- if (...state...): Out->In
- else: In->In
+ Tools:
+ Line
+ Flood
+ MeasuringTape
+ Pencil (Need to handle MouseLeft)
+ RopeEraser
+ RopePencil
+ Sampler
+ Selection
+ TextureEraser
+ TextureMagicWand
+ Make sure all mouse up's don't assume they are engaged
+ Cleanup all TODO's
+ All accesses to mouse coords: clamp to canvas when needed, do not rely on scissors
+ Identify and fix all tools that deal with View things that require scissors
+ LineTool
+ MeasuringTapeTool
+ Remove View::scissors afterwards
+ Merge to master
+ Tool base is not templated anymore - to allow for Ctrl^A/D
+ Make sure selection to selection manager is Rect (so that origin always in ship) and not two corners
+ Controller::GetCurrentToolAsSelectionTool -> SelectionTool& - with Tool::GetClass()->ToolClass {Other, Selection, Paste}
+ Ctrl^A accel => Controller::SelectAll()
+ If cur tool category not SelectionTool: change tool
+ Call Controller::GetCurrentToolAsSelectionTool().SelectAll()
+ Ctrl^D accel => Controller::Deselect()
+ Call Controller::GetCurrentToolAsSelectionTool().Deselect()
+ Publish size to StatusBar
+ Clone of OnMeasuredWorldLengthChanged
+ Between tool coordinates and sampled material
+ We have an icon for that, "selection_size"
+ Selection overlay: corners w/pixels
+ Fix issue:
+ ModelValidationDialog calls Controller::ValidateModel() from separate thread, which first suspends tool and then, when done, resumes the tool and
thus *creates* the tool (after our last change), causing OpenGL calls from separate thread; fix:
+ No thread in dialog
+ Controller::StartValidation() -> ModelValidationSession
+ Also suspends and stores scoped thing in Validator - cleared at Validator dctor
+ Verify
+ Dialog stores ValidationSession in opt session data and destroys when done
+ Timer advances Validator until it's done
+ And then destroy Validator in opt
+ Do lazy vector initialization at both Next() and GetCount()
+ And make it conditional
+ Tests:
+ "Fix issue" workflows
+ Save workflow
+ Copy & Cut:
+ Buttons at "Edit" ribbon (new own section)
+ Reconciliation with selection: enable/disable
+ private Controller::CopySelectionToClipboard()
+ Gets SelectionRect from SelectionManager, and LayerSelection from WorkbenchState::all-layers flag
+ Then gets LayersRegion from ModelController, and shoves it into ClipboardManager
+ Layer primitives:
+ Buffer2D:
+ Buffer<>::CloneRegion(Rect) -> Buffer
+ Buffer::PasteRegion(pos, Buffer)
+ Need to allow both pos and size to be outside of target buffer
+ So cannot be done in terms of BlitFromRegion
+ Add func operator
+ And one unit test for it
+ BlitFromRegion: takes responsibility of Paste
+ Nuke Paste afterwards
+ Renames: CloneRegion stays CloneRegion
+ New plan:
! Spec:
! "Buffer2D knows nothing about copy'n'paste"
X "ShipLayers only has methods for ShipFactory load options (e.g. Rotate, Flip, etc.)"
- Which anyway end up being used by ModelController as well
+ 1: ShipLayers: see if can make Structural optional; before we make yet another "ShipLayersRegion" class, try and
make ShipLayers with optional StructuralLayer, and Size (for flip/rotate)
+ Make sure Deserializers complain if no structural layer
X If not: make ShipLayersRegion and TODO: way to implement Rotate, Flip once
+ Remove ShipLayers::HasXXX methods
+ Test
+ Add layer + Undo, X all layer types
+ Remove lyaer + Undo, X all layer types
+ Struc Pencil tool + undo
+ Elect Pencil tool + undo
+ Resize larger + undo
+ Resize smaller + undo
+ Rotate + undo
X 2: Move XXXLayer() (and thus ShipLayers) methods that deal with Rope and Electric logic to ModelController,
except for Clone(), and for those needed by ShipFactory: Rotate(), Flip()
- TODO: leave this be for the time being - may be we don't need much duplication of elec panel and rope logic after all
- CloneRegion? Needed anywhere else?
- Trim? Needed anywhere else?
- MakeReframed? Needed anywhere else?
- Think of making proper class for ElectricalPanel
- Others?
X Rewrite/reroute whole Undo machinery @ Controller/ModelController/Model/Tools in terms of these
X TODO: think of changing tools' actions and eph visualizations wrt cloned regions: Model::Operation(...) returns LayerData
X Which then Undo action creator pairs with origin
X But watch out:
X Ropes' Undo: still best to take whole layer, no risk of losing ropes
X NOT A GOOD IDEA: methods would then be un-harmonic, for some layers would return region and not for others (e.g. Ropes)
X Also would be not performant (continuous copy & paste vs only blit)
+ ClipboardManager notifies IUserInterface @ population; used for: Paste
+ ModelController::Copy -> ShipLayers:
+ Some layers are just buffer::CloneRegion, Ropes is Copy
+ Test:
+ Individual layer types are picked up
+ "All layers" is picked up
+ Remove diagnostic logging
+ Controller::Cut():
+ Invoked by MainFrame
+ Calls CopySelectionToClipboard(...), then:
+ ModelController::EraseRegion(selection rect, which layers) => GenericUndo
+ "Generic Undo": class & ModelController Undo method
! Used for paste & cut undo, among eventually others
+ GenericUndoPayload: kinda like ShipLayers(Region), with differences:
+ Origin (coords)
+ Ropes and Electrical Panel are whole, other buffers are "regions"
+ Eventually, also ship metadata
+ private ModelController::MakeGenericUndoPayload(rect, layer selection)
+ public ModelController::Restore(GenericUndoPayload &&)
+ ModelController::EraseRegion(region, layer selection) -> GenericUndoPayload
+ Make undo via MakeGenericUndoPayload
+ Implement cut for each layer
+ Return undo
! Note: "Cut" Undo leaves clipboard populated - that's fine
+ Store GenericUndo in UndoStack
+ Tests:
+ GameViz bug:
+ Erase (Cut)
+ RestoreTextureLayerRegionBackup (Undo of cut)
+ MagicWand
+ Ephemeral texture eraser
+ Cut for each individual layer, & Undo
+ Ropes: completely contained, & undo
+ Ropes: partially contained, & undo
+ Ropes: not contained
+ Cut for all layers, & Undo
+ Pencil S & Undo
+ Pencil E & Undo
+ Verify bug existed and now fixed
+ Line S & Undo
+ Line E & Undo
+ Verify bug existed and now fixed
+ Flood & Undo
+ TextureMagicWand & Undo
+ TrimElectricalSubstratum & Undo
+ Controller::Copy():
+ Calls CopySelectionToClipboard()
+ MainFrame::ReconciliateUIWithSelection(): enable/disable Cut, Copy, Deselect buttons
+ Paste tool:
+ UI Reconciliation with clipboard being populated: enable/disable button
+ Initial call from Controller cctor
! Spec:
- Tool not selectable by user; auto-activated @ Paste
- Never enters "last tool" stack
! Tool's button is always disabled, except when active: is_enabled==is_active
- Tool gets own copy of LayersRegion at creation, and modifications happen to it (rather than to Clipboard's)
- Exited by:
- Selecting another tool
- In this case, it Commit's()
- Commit & Abort "Tool Settings" buttons
- Controller callbacks themselves tell PasteTool e.g. of abort, then destroy the tool
and activate previous one for the layer
- ~PasteTool() (e.g. because of Validation, etc.)
- In this case, it Abort's()
- How to distinguish from "Selecting another tool"? May be this one should Commit() as well
- PasteTool must not get resumed after a suspension
- Tool does Move; various "Tool Settings" buttons do rotate/flip/etc.
- Rotate/flip/etc: Button->Ctrl->PasteTool
- Flag for opaque vs transparent
- Checkbox->Ctrl->PasteTool
+ Impl:
+ Prelims:
+ Make Controlller::ToolType not optional
+ Get rid of WorkbenchState::CurrentToolType altogether
+ Controller::cctor:
+ Set current tool from last tool for current viz
+ Tests:
+ Suspend/Resume: MainFrame is being refreshed now; how does it look?
+ Removal of layers and thus change of primary viz layer => change of tool
+ Assets:
+ Paste tool icon: paste_icon.png
+ Cursor: pan_cursor.png
+ Tool button @ each layer
+ Reconciliation @ tool change: always disabled, except when active: is_enabled==is_active
+ Tool settings:
+ Layout is same as Selection
+ Flag for opaque vs transparent
+ -> Controller::SetPaste"Opaque/Transparentness"
+ Rotate/flip/etc
+ -> Controller::RotatePaste()/etc.
+ Separator
+ Commit/Abort
+ Assets:
+ confirm_40x40.png
+ x_40x40.png
+ Buttons
+ -> Controller::Commit/AbortPaste
+ Fix width: largest settings needs to be enabled at startup or else ribbon bar does size wide enough
+ Ctrl^V and button invoke Controller::Paste()
+ Controller::Paste():
+ 0a. Clone clipboard
+ ShipLayers::Clone
+ Unit Tests X 2 (full, empty)
+ 0b. Nuke current tool
+ 1. Decide ToolType value (i.e. layer)
+ First clipboard layer, and better if current viz layer
+ After choice: if different layer than current, change viz
+ Can use InternalSelectPrimaryVisualization, does not change tool
+ 2. Instantiate tool
+ Tool cctor takes clone of clipboard layers region (by move)
+ Tool cctor also takes flag for opaque vs transparent (comes from WorkbenchState)
+ 3. Set tool
+ Here the tool type does not enter stack of last used
+ "Normal" new tool select notification goes out to IUserInterface
+ Make sure Paste tool does not get resumed after a suspension
+ Controller::AbortPaste()
+ Get tool as PasteTool, and invoke Abort() on it
+ Then, destroy (reset) mCurrentTool, and activate previous one for the current layer
+ Controller::CommitPaste()
+ Get tool as PasteTool, and invoke Commit() on it
+ Then, destroy (reset) mCurrentTool, and activate previous one for the current layer
+ Controller::RotatePaste()/etc.
+ Get tool as PasteTool, and invoke Rotate()/etc. on it
+ Controller::SetPasteIsTransparent:
+ Invoked by MainFrame upon toggles of settings flag
+ Get tool as PasteTool, and invoke Set"Opaque/Transparentness"() on it
+ PasteTool:
+ State:
+ Eph viz: contains output of ModelController::PasteForEhpViz (which is GenericUndoPayload)
+ DrawEphViz()
+ Calls ModelController::PasteForEphViz(...) -> GenericUndoPayload
+ UndoEphViz()
+ Calls ModelController::~RestoreForEphViz(GenericUndoPayload)
+ ShipSpaceCoords mPasteOrigin
+ Initialized at cctor
+ Moved at MouseMove+LeftDown
+ mPasteRegion: optional; indicates "pending" (i.e. neither committed nor aborted)
+ Actually: optional should be of PasteRegion + MousePasteCoords, and eph viz
+ private DrawEphViz
+ assert(!eph viz)
+ MC::PasteForEphViz eph viz @ mPasteOrigin
+ View::UpdateOverlay
+ -> Undo in eph viz
+ private UndoEphemeralVisualization()
+ assert(eph viz)
+ MC::RestoreForEphViz
+ View::RemoveOverlay
+ cctor:
+ If cur mouse is visible: use it;
+- Else: calc mid of visible window -> mPasteOrigin
+ DrawEphViz
+ dctor:
! If pending: commit
+ If pending: Commit()
+ Abort():
! Undo eph viz, and make sure subsequent dctor does not see it as pending
+ assert (pending)
+ Reset eph viz (if any)
+ LayerChangeEpilog
+ Commit():
! Undo eph viz, Commit, and make sure subsequent dctor does not see it as pending
+ assert (pending)
+ Reset eph viz (if any)
+ ModelController::Paste
+ Make sure all affected layers are marked as dirty:
+ LayerChangeEpilog takes vector<LayerType>
+ ModelController exposes new vector<LayerType> CalculateLayerApplicability(ShipLayers)
+ Also use it at:
+ Paste
+ PasteForEph
+ 2 x Restore
+ Store Undo
+ No Undo if no layer affected
+ LayerChangeEpilog
+ Rotate()/etc.:
! Undo eph viz, Do operation on own mPasteRegion, Redo eph viz
+ Flip H/V
+ Rotate
+ Fix H/V aspect ratio issue:
+ Option 1:
+ Paste, PasteForEphViz: undo payload's texture must take into consideration actual texture region that will be pasted
+ ...which must take into consideration _source_ region as we can't do more than that
+ Ideally: both source and target affected rects have a texture counterpart (when payload has it) which takes into
account texture buffer
+ Set"Opaque/Transparentness" (for changes)
! Undo eph viz, change, Redo eph viz
+ MouseDown():
+ Get mouse coords; if in work canvas (OK even outside of ship):
+ Change mouse anchor
+ MouseUp():
+ Reset mMouseAnchor
+ MouseMove():
+ If mMouseAnchor:
+ Get mouse coords - anywhere
+ Reset eph viz (if any)
+ Calc stride and add it to mMousePasteCoords
= Clamp paste target to ship rect
+ DrawEphViz
+ LayerChangeEpilog
+ ShiftDown/Up:
+ Constrain H/V
+ Rethink PasteForEphViz/RestoreForEphViz: do own pure buffer blitting at both, storing into TODOHERE: MultiLayerBufferUndoPayload
+ Missing symmetry of methods:
! MakeGenericEphemeralVisualizationRestorePayload:
- S: - Undo: CloneRegion
- E: - Undo: CloneRegion
- R: - Undo: Clone
- T: - Undo: CloneRegion
! RestoreEphemeralVisualization:
- S: - Do: DoStructuralRegionBufferPaste
- E: - Do: DoElectricalRegionBufferPaste
- R: - Do: Move
- T: - Do: DoTextureRegionBufferPaste
------------------------------------------------
+ Option 1:
+ GenericUndoBufferPayload becomes GenericEphemeralVisualizationRestorePayload
+ Own file
+ PasteForEphViz uses helper (again) to make it: MakeGenericEphemeralVisualizationRestorePayload(...)
+ RestoreForEphViz becomes RestoreEphemeralVisualization(.)
+ FIX: RestoreEphemeralVisualization does not invalidate Ropes region!!!
+ Replace DoStructuralRegionBufferPaste/etc. calls in RestoreEphemeralVisualization with inline
+ Remove redundancies
+ S:
+ Helper: RestoreStructuralLayerRegionEphemeralVisualization
+ Use at:
+ RestoreEphemeralVisualization()
+ All tools
+ Test
+ E:
+ Do
+ Test
+ R:
+ Do
+ Test
+ T:
+ Do
+ Test
+ Fix skidmarks:
+ Both Paste and PasteForEphViz calculate affected rect - and most importantly affected source - and use those in their implementation, rather than letting
Blit() calc that
+ Try with texture (DoTextureRegionBufferPaste)
+ assert containments
+ Convert affected source rect to texture coords
+ Blit & mark dirty viz
+ Use it at all places:
+ Paste
+ PasteForEphViz
+ If works, do with all others as well
+ ModelController::Paste(LayersRegion, origin, opaque/transparent) X 2 (ephemeral, normal) -> GenericUndoPayload
+ PasteForEphemeralVisualization(...) -> GenericUndoPayload
+ blit (DoStructuralRegionBufferPaste)
+ blit (DoElectricalRegionBufferPaste)
+ blit (DoRopesRegionBufferPaste)
+ blit (DoTextureRegionBufferPaste)
+ RestoreEphemeralVisualization(GenericUndoPayload)
+ blit (RestoreStructuralLayerRegionEphemeralVisualization)
+ blit (RestoreElectricalLayerRegionEphemeralVisualization)
+ Whole ropes buffer (RestoreRopesLayerEphemeralVisualization)
+ blit (RestoreTextureLayerRegionEphemeralVisualization)
+ Paste(...) -> GenericUndoPayload
+ blit (DoStructuralRegionBufferPaste) & analysis re-initialization
+ blit (DoElectricalRegionBufferPaste), w/operator
+ Copy also electrical panel elements
+ ElectricalPanel
+ UnitTests
+ Use at ShipLayers - and etc.
+ Move ElectricalPanelElementMetadata from GameTypes to ElectricalPanel::ElementMetadata
+ Test
+ Do blit with operator
+ Make sure calls to InitializeElectricalLayerAnalysis know panel side-effects - NONE!
+ blit (DoRopesRegionBufferPaste)
+ blit (DoTextureRegionBufferPaste)
+ Restore(GenericUndoPayload)
+ Make sure elec instance ID factory is re-initialized
+ RopesBuffer::Blit
+ Code
+ Unit tests
+ Unify DoXXXRegionBufferPaste with RestoreXXXLayerRegionEphemeralVisualization
+ S
+ E
+ T
+ SelectionTool:
+ Change cursor @ OnMouseMove when not engaged and when at place for moving corner
+ Tests:
+ Ropes paste carries with it both and only ropes w/both endpoints in region, and with one endpoint in region
+ Make sure none of the Paste tools may be selected by user
+ After removing layer
+ Copy 1 layer and paste when that layer doesn't exist
+ Expect: no action at commit, no Undo (even if empty)
+ See if can do these other undo's with GenericUndo:
+ Resize (& Trim)
+ See if these are still needed afterwards:
+ MC::SetAllPresentLayersDirty
+ Re-test (w/undos)
+ Resize
+ Trim
+ Cut()
+ Flip
+ Rotate
+ RenderContext:
+ Single-threaded
+ Initializes on same canvas that main frame initializes on
+ Renders synchronously, on-demand via SB::MainFrame::OnPaint()
+ ViewModel:
+ Terminology:
+ WorkSpace: has the pixel size of the structure (equivalent of ::World)
+ WorkSpaceCoordinates (type @ ShipBuilderTypes)
+ DisplayLogical: has the logical display (window) size (equivalent of ::Logical)
+ DisplayLogicalCoordinates: the logical display coordinates (type @ ShipBuilderTypes)
+ DisplayPixel: has the pixel display (window) size (equivalent of ::Pixel)
+ DisplayPixelCoordinates: the pixel display coordinates (type @ ShipBuilderTypes)
+ World coords is model pixel coords, i.e. WorkSpace
+ Tells outside world of pan extent (for scrollbar width setting)
+ Primitives:
+ Background text
+ Structural RenderColor Texture
+ Electrical RenderColor Texture
+ Pseudo-cursor
+ One of many types; removed with a single call
+ WorkbenchState
+ Independent from model - exclusively tools-related settings
+ So no need to reset/change/update at new model creation
+ Saves and loads (some of) its settings from json
+ Validations:
+ Move validations to own class - ModelValidator
+ Exports one static method
+ Electrical connectivity validation
! Verifies that sources are connected to sinks and viceversa - both electrical and engine
+ Pass 1: detect "electrical sources" (generators), "electrical components" (anything that's supposed to be reachable, e.g. non-self-powered lamps, switches, engine controllers, etc.), "engine sources", "engine components"
+ Have 2D buffer of uint8_t union with flags, incl. "visited" flags
+ Pass 2: for each source & sink category:
+ Flood from each element, propagating through an element if it's flagged as "connector"
+ Use private helper for this
+ When done, detect all "opposites" (source->sink, sink->source) that are not reachable and complain
+ Electrical panel: not too many elements visible
- Visualizations:
- Electrical layer's "circuit" visualization
= Ship file format:
+ Fixed endianness
+ See https://github.com/voidah/archive/blob/master/archive.h
+ FS version
+ Only used for warnings in case materials not found and cur version > file version
+ "File lock" which disallows editing
! Password from user; salted -> Hash in Metadata
! Verify hash matches when editing
- Also includes sounds
- Extract from other "TODO" .txt file
- Ubuntu:
- Test User Ships folder
- Misc:
- Localization:
- See if can do separate extraction into separate file, which then is loaded at all times by LocalizationManager together with the other one
- Ruler: pick a scale (pixels to meter of original ship), then you choose how long the ruler
- L shaped: horizontal and vertical
- Ruler sticks to the canvas
- Only one ruler at a time
- Ship bootstrapper from texture
- Uses fg and bg color for contour/interior
- Selectable backgrounds
- Inherits FullScreen state and shares state changes (so, for example, if exits full screen in ShipBuilder, also Game has exited full screen)
- How to share state changes?
- FullScreenManager class, shared_ptr in both
- Looked at at each Game<->ShipBuilder switch by receiver
+ Windows installer (or ShipBuilder::MainFrame cctor): on Windows, register shell icon handler for .shp2 files, and icon
- Each time we StartTool() (e.g. at AddLayer, etc.) we end up refreshing modelcontroller's visualization and OpenGL visualization, twice
- TO-SPEC:
- What to do with background music when switching back and forth?
- Ability to import an image to trace over it
- Like a texture layer, but not saved and not part of the ship
- MainFrame: add "export to image" button (in non-layer-specific ribbon) - basically a Screenshot tool
- Screenshot for the current visualization(s), i.e. the canvas render buffer as it is
- MaterialsPalette: make flag for making it its own wxFrame, so one can use it in a second monitor
- Ephemeral visualization rearch:
- Current structure:
- Tool::ctor, Tool::EndEngagement: Take orig clone
- @ Do:
- DoFillForTempVisualization
- Layer + Render buffer, ONLY
- DoFillForTempVisualization
- DoFillForTempVisualization ...
- UploadVisualization
- RefreshView
- @ Mend, @ End:
- RestoreStructuralLayerRegionForEphemeralVisualization(orig clone)
- UploadVisualization
- RefreshView
! Total cost:
- Tool create: 1 alloc + 1 full copy
- Tool move: 1 region blit + 1 region fill + 1 full upload
- Tool end: 1 region blit + 1 full upload
- Proposed structure 1: token (aka "session")
- Spec:
- High-level:
- The "real" model is never changed during temp visualization
- The token/session wraps ModelController, providing the latter with own buffers when doing ops on temp viz
- ModelController operations that happen on both model and temp viz model are implemented generically so they accept external buffers as well
- Outsourced data:
- Layer itself
- Layer viz + its Dirty region == struct LayerVisualization { uq_ptr<RgbaImageData>, opt<Rect> }
- Tool::ctor, Tool::EndEngagement:
- ModelController::InitiateTempVisualization() -> TempVisualizationToken
- TempVisualizationToken::cctor: take orig clone
- Tool stores token
- @ Do:
- TempVisualizationToken::DoFillForTempVisualization
- Forwarded to ModelController
- TempVisualizationToken::DoFillForTempVisualization
- TempVisualizationToken::DoFillForTempVisualization ...
- TempVisualiationToken::Render
- UploadVisualization
- RefreshView
- @ Mend:
- TempVisualizationToken::UndoTempVisualization
- RestoreStructuralLayerRegionForEphemeralVisualization(orig clone)
- UploadVisualization
- RefreshView
- @ End (dctor):
- Same as @Mend
! Total cost:
- Tool create: 1 alloc + 1 full copy
- Tool move: 1 region blit + 1 region fill + 1 full upload
- Tool end: 1 full upload
- Details:
- ModelLayerEphemeralVisualizationSession<TLayer>
+ Not copyable, yes moveable
- Members:
- References to arch members:
- ModelController &
- TLayer mOriginalLayerClone
- LayerVisualization mWorkVisualization
- Session:
- DirtyRegion == std::opt<Rect>
- cctor(ModelController &):
+ Take original layer clone
+ Initialize session
+ DirtyRegion=nullopt
- Fill(rect, material)
-
- Mend()
-
- ~dctor():
-
- TODOs:
+ Clone rearch
--------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------
DONE
--------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------
+ Move/merge integral coords/size from shipbuilder to game
+ Game: "ShipSpace" and "DisplayPhysical/Logical"
+ ShipBuilder
+ SB::ViewModel: ortho matrix: see again why ShipSpace says positive bottom
+ Anchor to the top
+ LayerBuffer.h: "layer buffer" types
+ StructuralLayerBuffer == Buffer2D<StructuralElement, ShipSpace>
+ StructuralElement = <StructuralMaterial*>
+ ElectricalLayerBuffer == Buffer2D<ElectricalElement, ShipSpace>
+ ElectricalMaterial = <ElectricalMaterial*, instance ID>
+ RopesLayerBuffer == Buffer2D<RopeElement, ShipSpace>
+ RopeElement = <StructuralMaterial*, endpoint ID>
+ TextureLayerBuffer == Buffer2D<rgbaColor, ImageSpace>
+ Use "layer buffers" at ShipFactory
+ ShipMaterialization class (@GameLib): four buffers
+ static ShipFactory::MaterializeShip(...) -> ShipMaterialization
+ See first if can make whole ShipFactory static again
+ Also includes filling-in Ropes from rope endpoints found in structure (legacy)
+ Throw on conflicts, like (should be) now
+ Also includes filling-in Electrical from electrical materials found in structure (legacy)
+ Throw on conflicts, like (should be) now
+ ShipFactory::WhateverNow(): use MaterializeShip to get ShipMaterialization and continue from there
+ Given that you're at it: ShipFactoryTypes::ShipFactoryPoint: nuke vec2i and replace with ShipSpaceCoordinates (and user-facing coords being ImageCoordinates)
+ Test:
+ Basic:
+ Legacy ropes
+ Native ropes
X Legacy+Native ropes
+ Legacy electrical
+ Native electrical
X Legacy+Native electrical
+ Texture
+ Corner cases:
+ Ropes: too many endpoints for same Rope ID
+ All legacy
+ All native
+ Both auto-texturization settings
+ Perf (before & after)
+ Before: ~100ms (factory including auto-text)
+ After: ~110ms
+ Check if also old code asserted on Szerszen cargo for hull
+ Ship De/Serialization
! Spec:
- Ultimate ship definition consists of LayerBuffers, not images (except for Texture obviously)
- ShipMaterialization functionality currently in ShipFactory should move to legacy ship deserializer, for formats "png" and "shp"
- This is where we're fine with doing the "legacy" tricks, and with e.g. inserting missing structural materials for rope endpoints in the ropes layer
+ Impl:
+ ShipMaterialization also carries all attributes of ShipDefinition
+ ShipDeserializer
+ Load(filepath, MaterialDB) -> ShipMaterialization
+ ShipMaterialization functionality currently in ShipFactory should move to legacy ship deserializer, for formats "png" and "shp"
+ This is where we're fine with doing the "legacy" tricks, and with e.g. inserting missing structural materials for rope endpoints in the ropes layer
+ png
+ shp
+ LoadPreview(filepath, MaterialDB) -> ShipPreview
+ png
+ shp
+ Save(ShipMaterialization, filepath)
+ NOP at the moment
+ Nuke ShipPreview.cpp
+ Nuke ShipDefinition.*, ShipDefinitionFile.*
+ "ShipMaterialization" -> "ShipDefinition"
+ Use ShipDeSerializer ipv ShipDefinition::Load
+ ShipDefinition
+ ShipPreview
+ ShipFactory:
+ Nuke MaterializeShip code, just begin with ShipMaterialization (now ShipDefinition) as arg
+ Review whole need of ropes creating structural endpoints @ load type from .shp files
+ Do at ShipFactory::Create() within the first pass on Ropes (i.e. ExtractRopeSegments => PostProcessRopesLayer())
+ Fix Szerszen
+ Fix hull assert
+ Fix RopesTest for Frontiers crash
+ And subsequently, check the need for StructuralElement to have RenderColor
+ Model contains the four new layer buffer types (1 as member, 3 as uq_ptr)
+ Merge into shipbuilder
+ Undo
+ Hierarchy: there are four subtypes of UndoEditAction - for each of the "layer buffer" types - on top of e.g. "resize" and "all layers"
+ These four are all implemented via a single templated class, templated on TLayerBuffer
+ Contains TLayerBuffer and origin
+ Apply() invokes Controller::RestoreLayer(TLayerBuffer const &, origin)
+ Dirtyness:
+ Model::DirtyState == {array, metadata, ...}, w/=operator
+ Base UndoAction has DirtyState member, given via cctor & exposed via getter
+ Controller::Undo()
+ Auto-zoom on new ModelController
+ By Controller, at cctor (i.e. after CreateNew() and CreateForShip())
+ Via ViewModel->CalculateIdealZoom() const -> zoom, then set in ViewModel
+ Grid view:
+ Button right of transparency slider
+ Controller->View
+ View takes it in cctor
+ Shader
+ Toolbar rearc:
+ File panel
+ Ship settings panel
+ Resize
+ Ship metadata
+ Tool settings panel
+ Check in-game
+ Replace "Yes/No are you sure" with "Do you want to save your changes before continuing?" -> Yes, No, Cancel
+ "AskUserIfSure" -> "AskUserIfSave" => returns int (wxID_YES, wxID_NO, wxID_CANCEL)
+ On yes, call SaveShip(), and if the latter returns false, take the Cancel route
+ ShipLoadDialog: doesn't catch new file
! It's because at open we don't scan directory unless we've changed directory
+ Solution:
+ At open, if haven't changed dir, build map (ordered) of <filepath, last_modif>
+ Then compare map with same map built at PreviewReady handling
+ Requires message to also contain last_modif
+ If changed: follow same path as if directory changed
+ Also: make snapshot as above, and then also passes it to thread as replacement of thread's first step
+ Move ShipPreviewDirectoryManager::EnumerateShipFilePaths to here basically, improving it as we may avoid storing files if not ship files
+ New/Load/Save Ship
+ Move ShipLoadDialog/etc. under UILib
+ Test from game:
+ Last directory is added to both ComboBox and UIPreferences
+ Yes when loaded
+ No when not loaded
+ Show ship descriptions:
+ Works in dialog
+ Works at load
+ User may override
+ Second home button
+ New
+ UI: menu + button
+ Always enabled
+ Asks user if model dirty
+ Invoke DoNewShip()
+ Load
+ UI: menu + button
+ Always enabled
+ Asks user if model dirty
+ Open file load dialog
+ If OK: invoke DoLoadShip(selected filename)
+ TEST: in Game, 2 dialogs
+ Save:
+ UI: menu + button
+ Only enabled when dirty
+ if !mCurrentFilename: invokes SaveAs(); else: invokes private SaveShip(mCurrentFilename)
+ SaveAs:
+ UI: menu + button
+ Only enabled when dirty
+ Open file save dialog for .shp2
+ If OK:
+ Invoke private SaveShip(selected filename)
+ DoNewShip:
+ Do
+ DoLoadShip:
+ Do
+ Store filename as mCurrentFilename
+ DoSaveShip:
+ Ship Preview workflow:
+ ShipDeSerializer: split out legacy into own class
+ ShipDeSerializer: LoadShipPreviewData() and LoadShipPreviewImage(ShipPreviewData const &)
+ ShipPreview ("ShipPreviewData") from .shp2 contains .shp2 filepath itself
+ ShipDeSerializer::LoadShipPreviewImage: if type is shp2, call ShipDefinitionFormatDeSerializer::LoadPreviewImage(filepath)
+ else: type can only be png (throw if not) - call legacy LoadPreviewImage() which loads image
+ Then resize and trim (like it was @ ShipPreview)
+ .shp2 format: preview is own tag, image chunk saved at ship save time - but only if there's no texture
+ Order of chunks:
+ If has texture: metadata, texture, ...
+ Else: metadata, preview, ...
+ Do LoadPreview
+ Have one single main loop for the three load calls
+ ShipDeSerializer::Save(ShipDefinition const &)
+ Rearc exceptions:
+ GameCore/UserGameException
+ With enum
+ Exception contains enum and string array
+ LocalizationManager::MakeErrorMessage(LocalizableGameException const &)
+ Formats
+ Catch-&-localize @ callers
+ Game
+ ShipBuilder
+ Preview
+ Individual methods throw directly LocalizableGameException, no post-processing
+ DeSerializationContext: private class with file FS version, stored as pair of integers
+ Nuke MajorMinorVersion class
+ Unit tests for all exceptions
+ Header
+ Unrecognized material in structural layer
+ Test all w/localized strings
+ Game
+ ShipBuilder