diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..567609b --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c9e8a38 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Orito Itsuki + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..5999647 --- /dev/null +++ b/README.md @@ -0,0 +1,56 @@ +

Aquamarine Painter

+ +

+ +

+ +

+[ja | en] +

+ +## スクリーンショット + +https://github.com/user-attachments/assets/64b61a46-360f-4107-a62b-e9491fa3b449 + +![screenshot](./README/screenshot-0.png) + +![screenshot](./README/screenshot-1.png) + +## PPW Curves + +このソフトウェアでは[PPW Curves](https://www.jstage.jst.go.jp/article/transinf/E105.D/10/E105.D_2022PCP0006/_pdf)というスプライン曲線を利用しています。 + +この曲線はすべてのコントロールポイントを通る曲線で、調整ツールを利用することで以下のようなパラメータの調整ができます。 + +| 操作 | gif | +| --- | --- | +| コントロールポイントを左右ドラッグ | ![weight](./README/weight.gif) | +| コントロールポイントの間のセグメントを上下にドラッグ | ![phi](./README/phi.gif) | +| コントロールポイントの間のセグメントを左右にドラッグ | ![psi](./README/psi.gif) | + +## キーボードショートカット + +| キー | 機能 | +| --- | --- | +| `Ctrl` + `Z` | アンドゥ | +| `Ctrl` + `Shift` + `Z` | リドゥ | +| `Ctrl` + `C` | 選択中のマテリアル or レイヤーのコピー | +| `Ctrl` + `V` | ペースト | +| `1` | 描画ツール | +| `2` | 操作ツール | +| `3` | 調整ツール | +| `4` | 枠内に収める | +| `5` | 拡大率を100%に戻す | +| `6` | 左右反転 | +| `Space` | コントローラーの一時非表示 | + +## 免責事項 + +このアプリは個人用に作成したものであり、OSSプロジェクトとして機能追加・要望対応などを行っていく予定はありません。 +このアプリのソースコードはMPL-2.0ライセンスで配布しているので、必要があればforkして自分で機能を追加してください。 + +## LICENSE + +このアプリのソースコードはMITライセンスで配布しています。 + +一部にMPL-2.0ライセンスのプログラムを利用しています。 diff --git a/README/README-en.md b/README/README-en.md new file mode 100644 index 0000000..0d08127 --- /dev/null +++ b/README/README-en.md @@ -0,0 +1,56 @@ +

Aquamarine Painter

+ +

+ +

+ +

+[ja | en] +

+ +## Screenshots + +https://github.com/user-attachments/assets/64b61a46-360f-4107-a62b-e9491fa3b449 + +![screenshot](./screenshot-0.png) + +![screenshot](./screenshot-1.png) + +## PPW Curves + +This software uses a spline curve called [PPW Curves](https://www.jstage.jst.go.jp/article/transinf/E105.D/10/E105.D_2022PCP0006/_pdf). + +This curve passes through all the control points, and you can adjust the following parameters using the adjustment tool. + +| Operation | gif | +| --- | --- | +| Drag the control points left and right. | ![weight](./weight.gif) | +| Drag the segment between the control points up and down. | ![phi](./phi.gif) | +| Drag the segment between the control points left and right. | ![psi](./psi.gif) | + +## Keyboard Shortcuts + +| Key | Function | +| --- | --- | +| `Ctrl` + `Z` | Undo | +| `Ctrl` + `Shift` + `Z` | Redo | +| `Ctrl` + `C` | Copy selected material or layer | +| `Ctrl` + `V` | Paste | +| `1` | Painting Tools | +| `2` | Operation Tools | +| `3` | Adjustment Tools | +| `4` | Fit in Frame | +| `5` | Reset Zoom to 100% | +| `6` | Flip Horizontal | +| `Space` | Temporary hiding of controllers | + +## Disclaimer + +This app was created for personal use, and there are no plans to add features or respond to requests as an OSS project. +The source code for this app is distributed under the MPL-2.0 license, so if you need to, please fork it and add your own functions. + +## LICENSE + +The source code for this application is distributed under the MIT license. + +This program uses MPL-2.0 licensed programs. diff --git a/README/phi.gif b/README/phi.gif new file mode 100644 index 0000000..50b95aa Binary files /dev/null and b/README/phi.gif differ diff --git a/README/psi.gif b/README/psi.gif new file mode 100644 index 0000000..4eb920f Binary files /dev/null and b/README/psi.gif differ diff --git a/README/screenshot-0.png b/README/screenshot-0.png new file mode 100644 index 0000000..70fdabe Binary files /dev/null and b/README/screenshot-0.png differ diff --git a/README/screenshot-1.png b/README/screenshot-1.png new file mode 100644 index 0000000..812ec5c Binary files /dev/null and b/README/screenshot-1.png differ diff --git a/README/top.png b/README/top.png new file mode 100644 index 0000000..2ff4a6e Binary files /dev/null and b/README/top.png differ diff --git a/README/weight.gif b/README/weight.gif new file mode 100644 index 0000000..522aee3 Binary files /dev/null and b/README/weight.gif differ diff --git a/godot/.gitattributes b/godot/.gitattributes new file mode 100644 index 0000000..8ad74f7 --- /dev/null +++ b/godot/.gitattributes @@ -0,0 +1,2 @@ +# Normalize EOL for all files that Git considers text files. +* text=auto eol=lf diff --git a/godot/.gitignore b/godot/.gitignore new file mode 100644 index 0000000..3294fbf --- /dev/null +++ b/godot/.gitignore @@ -0,0 +1,3 @@ +# Godot 4+ specific ignores +.godot/ +.vscode/ diff --git a/godot/RobotoMono-SemiBold.ttf b/godot/RobotoMono-SemiBold.ttf new file mode 100644 index 0000000..82ddc82 Binary files /dev/null and b/godot/RobotoMono-SemiBold.ttf differ diff --git a/godot/RobotoMono-SemiBold.ttf.import b/godot/RobotoMono-SemiBold.ttf.import new file mode 100644 index 0000000..96ba277 --- /dev/null +++ b/godot/RobotoMono-SemiBold.ttf.import @@ -0,0 +1,34 @@ +[remap] + +importer="font_data_dynamic" +type="FontFile" +uid="uid://detoutj1j8iin" +path="res://.godot/imported/RobotoMono-SemiBold.ttf-af5a063eb39e9bae115cbd93617ff3f6.fontdata" + +[deps] + +source_file="res://RobotoMono-SemiBold.ttf" +dest_files=["res://.godot/imported/RobotoMono-SemiBold.ttf-af5a063eb39e9bae115cbd93617ff3f6.fontdata"] + +[params] + +Rendering=null +antialiasing=1 +generate_mipmaps=false +disable_embedded_bitmaps=true +multichannel_signed_distance_field=false +msdf_pixel_range=8 +msdf_size=48 +allow_system_fallback=true +force_autohinter=false +hinting=1 +subpixel_positioning=1 +oversampling=0.0 +Fallbacks=null +fallbacks=[] +Compress=null +compress=true +preload=[] +language_support={} +script_support={} +opentype_features={} diff --git a/godot/addons/dockable_container/LICENSE b/godot/addons/dockable_container/LICENSE new file mode 100644 index 0000000..0e259d4 --- /dev/null +++ b/godot/addons/dockable_container/LICENSE @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. diff --git a/godot/addons/dockable_container/dockable_container.gd b/godot/addons/dockable_container/dockable_container.gd new file mode 100644 index 0000000..3913a68 --- /dev/null +++ b/godot/addons/dockable_container/dockable_container.gd @@ -0,0 +1,448 @@ +@tool +class_name DockableContainer +extends Container + +const SplitHandle := preload("split_handle.gd") +const DockablePanel := preload("dockable_panel.gd") +const DragNDropPanel := preload("drag_n_drop_panel.gd") + +@export var tab_alignment := TabBar.ALIGNMENT_CENTER: + get: + return _tab_align + set(value): + _tab_align = value + for i in range(1, _panel_container.get_child_count()): + var panel := _panel_container.get_child(i) as DockablePanel + panel.tab_alignment = value +@export var use_hidden_tabs_for_min_size := false: + get: + return _use_hidden_tabs_for_min_size + set(value): + _use_hidden_tabs_for_min_size = value + for i in range(1, _panel_container.get_child_count()): + var panel := _panel_container.get_child(i) as DockablePanel + panel.use_hidden_tabs_for_min_size = value +@export var tabs_visible := true: + get: + return _tabs_visible + set(value): + _tabs_visible = value + for i in range(1, _panel_container.get_child_count()): + var panel := _panel_container.get_child(i) as DockablePanel + panel.show_tabs = _tabs_visible +## If [code]true[/code] and a panel only has one tab, it keeps that tab hidden even if +## [member tabs_visible] is [code]true[/code]. +## Only takes effect is [member tabs_visible] is [code]true[/code]. +@export var hide_single_tab := false: + get: + return _hide_single_tab + set(value): + _hide_single_tab = value + for i in range(1, _panel_container.get_child_count()): + var panel := _panel_container.get_child(i) as DockablePanel + panel.hide_single_tab = _hide_single_tab +@export var rearrange_group := 0 +@export var layout := DockableLayout.new(): + get: + return _layout + set(value): + set_layout(value) +## If `clone_layout_on_ready` is true, `layout` will be cloned checked `_ready`. +## This is useful for leaving layout Resources untouched in case you want to +## restore layout to its default later. +@export var clone_layout_on_ready := true + +var _layout := DockableLayout.new() +var _panel_container := Container.new() +var _split_container := Container.new() +var _drag_n_drop_panel := DragNDropPanel.new() +var _drag_panel: DockablePanel +var _tab_align := TabBar.ALIGNMENT_CENTER +var _tabs_visible := true +var _use_hidden_tabs_for_min_size := false +var _hide_single_tab := false +var _current_panel_index := 0 +var _current_split_index := 0 +var _children_names := {} +var _layout_dirty := false + + +func _init() -> void: + child_entered_tree.connect(_child_entered_tree) + child_exiting_tree.connect(_child_exiting_tree) + + +func _ready() -> void: + set_process_input(false) + _panel_container.name = "_panel_container" + add_child(_panel_container) + move_child(_panel_container, 0) + _split_container.name = "_split_container" + _split_container.mouse_filter = MOUSE_FILTER_PASS + _panel_container.add_child(_split_container) + + _drag_n_drop_panel.name = "_drag_n_drop_panel" + _drag_n_drop_panel.mouse_filter = MOUSE_FILTER_PASS + _drag_n_drop_panel.visible = false + add_child(_drag_n_drop_panel) + + if not _layout: + set_layout(null) + elif clone_layout_on_ready and not Engine.is_editor_hint(): + set_layout(_layout.clone()) + + +func _notification(what: int) -> void: + if what == NOTIFICATION_SORT_CHILDREN: + _resort() + elif ( + what == NOTIFICATION_DRAG_BEGIN + and _can_handle_drag_data(get_viewport().gui_get_drag_data()) + ): + _drag_n_drop_panel.set_enabled(true, not _layout.root.is_empty()) + set_process_input(true) + elif what == NOTIFICATION_DRAG_END: + _drag_n_drop_panel.set_enabled(false) + set_process_input(false) + + +func _input(event: InputEvent) -> void: + assert(get_viewport().gui_is_dragging(), "FIXME: should only be called when dragging") + if event is InputEventMouseMotion: + var local_position := get_local_mouse_position() + var panel: DockablePanel + for i in range(1, _panel_container.get_child_count()): + var p := _panel_container.get_child(i) as DockablePanel + if p.get_rect().has_point(local_position): + panel = p + break + _drag_panel = panel + if not panel: + return + fit_child_in_rect(_drag_n_drop_panel, panel.get_child_rect()) + + +func _child_entered_tree(node: Node) -> void: + if node == _panel_container or node == _drag_n_drop_panel: + return + _drag_n_drop_panel.move_to_front() + _track_and_add_node(node) + + +func _child_exiting_tree(node: Node) -> void: + if node == _panel_container or node == _drag_n_drop_panel: + return + _untrack_node(node) + + +func _can_drop_data(_position: Vector2, data) -> bool: + return _can_handle_drag_data(data) + + +func _drop_data(_position: Vector2, data) -> void: + var from_node := get_node(data.from_path) + if from_node is TabBar: + from_node = from_node.get_parent() + if from_node == _drag_panel and _drag_panel.get_child_count() == 1: + return + var tab_index = data.tabc_element if data.has("tabc_element") else data.tab_index + var moved_tab = from_node.get_tab_control(tab_index) + if moved_tab is DockableReferenceControl: + moved_tab = moved_tab.reference_to + if not _is_managed_node(moved_tab): + moved_tab.get_parent().remove_child(moved_tab) + add_child(moved_tab) + + if _drag_panel != null: + var margin := _drag_n_drop_panel.get_hover_margin() + _layout.split_leaf_with_node(_drag_panel.leaf, moved_tab, margin) + + _layout_dirty = true + queue_sort() + + +func set_control_as_current_tab(control: Control) -> void: + assert( + control.get_parent_control() == self, + "Trying to focus a control not managed by this container" + ) + if is_control_hidden(control): + push_warning("Trying to focus a hidden control") + return + var leaf := _layout.get_leaf_for_node(control) + if not leaf: + return + var position_in_leaf := leaf.find_child(control) + if position_in_leaf < 0: + return + var panel: DockablePanel + for i in range(1, _panel_container.get_child_count()): + var p := _panel_container.get_child(i) as DockablePanel + if p.leaf == leaf: + panel = p + break + if not panel: + return + panel.current_tab = clampi(position_in_leaf, 0, panel.get_tab_count() - 1) + + +func set_layout(value: DockableLayout) -> void: + if value == null: + value = DockableLayout.new() + if value == _layout: + return + if _layout and _layout.changed.is_connected(queue_sort): + _layout.changed.disconnect(queue_sort) + _layout = value + _layout.changed.connect(queue_sort) + _layout_dirty = true + queue_sort() + + +func set_use_hidden_tabs_for_min_size(value: bool) -> void: + _use_hidden_tabs_for_min_size = value + for i in range(1, _panel_container.get_child_count()): + var panel = _panel_container.get_child(i) + panel.use_hidden_tabs_for_min_size = value + + +func get_use_hidden_tabs_for_min_size() -> bool: + return _use_hidden_tabs_for_min_size + + +func set_control_hidden(child: Control, is_hidden: bool) -> void: + _layout.set_node_hidden(child, is_hidden) + + +func is_control_hidden(child: Control) -> bool: + return _layout.is_node_hidden(child) + + +func get_tabs() -> Array[Control]: + var tabs: Array[Control] = [] + for i in get_child_count(): + var child := get_child(i) + if _is_managed_node(child): + tabs.append(child) + return tabs + + +func get_tab_count() -> int: + var count := 0 + for i in get_child_count(): + var child := get_child(i) + if _is_managed_node(child): + count += 1 + return count + + +func _can_handle_drag_data(data) -> bool: + if data is Dictionary and data.get("type") in ["tab_container_tab", "tabc_element"]: + var tabc := get_node_or_null(data.get("from_path")) + return ( + tabc + and tabc.has_method("get_tabs_rearrange_group") + and tabc.get_tabs_rearrange_group() == rearrange_group + ) + return false + + +func _is_managed_node(node: Node) -> bool: + return ( + node.get_parent() == self + and node != _panel_container + and node != _drag_n_drop_panel + and node is Control + and not node.top_level + ) + + +func _update_layout_with_children() -> void: + var names := PackedStringArray() + _children_names.clear() + for i in range(1, get_child_count() - 1): + var c := get_child(i) + if _track_node(c): + names.append(c.name) + _layout.update_nodes(names) + _layout_dirty = false + + +func _track_node(node: Node) -> bool: + if not _is_managed_node(node): + return false + _children_names[node] = node.name + _children_names[node.name] = node + if not node.renamed.is_connected(_on_child_renamed): + node.renamed.connect(_on_child_renamed.bind(node)) + if not node.tree_exiting.is_connected(_untrack_node): + node.tree_exiting.connect(_untrack_node.bind(node)) + return true + + +func _track_and_add_node(node: Node) -> void: + var tracked_name = _children_names.get(node) + if not _track_node(node): + return + if tracked_name and tracked_name != node.name: + _layout.rename_node(tracked_name, node.name) + _layout_dirty = true + + +func _untrack_node(node: Node) -> void: + _children_names.erase(node) + _children_names.erase(node.name) + if node.renamed.is_connected(_on_child_renamed): + node.renamed.disconnect(_on_child_renamed) + if node.tree_exiting.is_connected(_untrack_node): + node.tree_exiting.disconnect(_untrack_node) + _layout_dirty = true + + +func _resort() -> void: + assert(_panel_container, "FIXME: resorting without _panel_container") + if _panel_container.get_index() != 0: + move_child(_panel_container, 0) + if _drag_n_drop_panel.get_index() < get_child_count() - 1: + _drag_n_drop_panel.move_to_front() + + if _layout_dirty: + _update_layout_with_children() + + var rect := Rect2(Vector2.ZERO, size) + fit_child_in_rect(_panel_container, rect) + _panel_container.fit_child_in_rect(_split_container, rect) + + _current_panel_index = 1 + _current_split_index = 0 + + var children_list := [] + _calculate_panel_and_split_list(children_list, _layout.root) + _fit_panel_and_split_list_to_rect(children_list, rect) + + _untrack_children_after(_panel_container, _current_panel_index) + _untrack_children_after(_split_container, _current_split_index) + + +## Calculate DockablePanel and SplitHandle minimum sizes, skipping empty +## branches. +## +## Returns a DockablePanel checked non-empty leaves, a SplitHandle checked non-empty +## splits, `null` if the whole branch is empty and no space should be used. +## +## `result` will be filled with the non-empty nodes in this post-order tree +## traversal. +func _calculate_panel_and_split_list(result: Array, layout_node: DockableLayoutNode): + if layout_node is DockableLayoutPanel: + var nodes: Array[Control] = [] + for n in layout_node.names: + var node: Control = _children_names.get(n) + if node: + assert(node is Control, "FIXME: node is not a control %s" % node) + assert( + node.get_parent_control() == self, + "FIXME: node is not child of container %s" % node + ) + if is_control_hidden(node): + node.visible = false + else: + nodes.append(node) + if nodes.is_empty(): + return null + else: + var panel := _get_panel(_current_panel_index) + _current_panel_index += 1 + panel.track_nodes(nodes, layout_node) + result.append(panel) + return panel + elif layout_node is DockableLayoutSplit: + # by processing `second` before `first`, traversing `result` from back + # to front yields a nice pre-order tree traversal + var second_result = _calculate_panel_and_split_list(result, layout_node.second) + var first_result = _calculate_panel_and_split_list(result, layout_node.first) + if first_result and second_result: + var split := _get_split(_current_split_index) + _current_split_index += 1 + split.layout_split = layout_node + split.first_minimum_size = first_result.get_layout_minimum_size() + split.second_minimum_size = second_result.get_layout_minimum_size() + result.append(split) + return split + elif first_result: + return first_result + else: # NOTE: this returns null if `second_result` is null + return second_result + else: + push_warning("FIXME: invalid Resource, should be branch or leaf, found %s" % layout_node) + + +## Traverse list from back to front fitting controls where they belong. +## +## Be sure to call this with the result from `_calculate_split_minimum_sizes`. +func _fit_panel_and_split_list_to_rect(panel_and_split_list: Array, rect: Rect2) -> void: + var control = panel_and_split_list.pop_back() + if control is DockablePanel: + _panel_container.fit_child_in_rect(control, rect) + elif control is SplitHandle: + var split_rects = control.get_split_rects(rect) + _split_container.fit_child_in_rect(control, split_rects["self"]) + _fit_panel_and_split_list_to_rect(panel_and_split_list, split_rects["first"]) + _fit_panel_and_split_list_to_rect(panel_and_split_list, split_rects["second"]) + + +## Get the idx'th DockablePanel, reusing an instanced one if possible +func _get_panel(idx: int) -> DockablePanel: + assert(_panel_container, "FIXME: creating panel without _panel_container") + if idx < _panel_container.get_child_count(): + return _panel_container.get_child(idx) + var panel := DockablePanel.new() + panel.tab_alignment = _tab_align + panel.show_tabs = _tabs_visible + panel.hide_single_tab = _hide_single_tab + panel.use_hidden_tabs_for_min_size = _use_hidden_tabs_for_min_size + panel.set_tabs_rearrange_group(maxi(0, rearrange_group)) + _panel_container.add_child(panel) + panel.tab_layout_changed.connect(_on_panel_tab_layout_changed.bind(panel)) + return panel + + +## Get the idx'th SplitHandle, reusing an instanced one if possible +func _get_split(idx: int) -> SplitHandle: + assert(_split_container, "FIXME: creating split without _split_container") + if idx < _split_container.get_child_count(): + return _split_container.get_child(idx) + var split := SplitHandle.new() + _split_container.add_child(split) + return split + + +## Helper for removing and freeing all remaining children from node +func _untrack_children_after(node: Control, idx: int) -> void: + for i in range(idx, node.get_child_count()): + var child := node.get_child(idx) + node.remove_child(child) + child.queue_free() + + +## Handler for `DockablePanel.tab_layout_changed`, update its DockableLayoutPanel +func _on_panel_tab_layout_changed(tab: int, panel: DockablePanel) -> void: + _layout_dirty = true + var control := panel.get_tab_control(tab) + if control is DockableReferenceControl: + control = control.reference_to + if not _is_managed_node(control): + control.get_parent().remove_child(control) + add_child(control) + _layout.move_node_to_leaf(control, panel.leaf, tab) + queue_sort() + + +## Handler for `Node.renamed` signal, updates tracked name for node +func _on_child_renamed(child: Node) -> void: + var old_name: String = _children_names.get(child) + if old_name == str(child.name): + return + _children_names.erase(old_name) + _children_names[child] = child.name + _children_names[child.name] = child + _layout.rename_node(old_name, child.name) diff --git a/godot/addons/dockable_container/dockable_panel.gd b/godot/addons/dockable_container/dockable_panel.gd new file mode 100644 index 0000000..d522027 --- /dev/null +++ b/godot/addons/dockable_container/dockable_panel.gd @@ -0,0 +1,108 @@ +@tool +extends TabContainer + +signal tab_layout_changed(tab) + +var leaf: DockableLayoutPanel: + get: + return get_leaf() + set(value): + set_leaf(value) +var show_tabs := true: + get: + return _show_tabs + set(value): + _show_tabs = value + _handle_tab_visibility() +var hide_single_tab := false: + get: + return _hide_single_tab + set(value): + _hide_single_tab = value + _handle_tab_visibility() + +var _leaf: DockableLayoutPanel +var _show_tabs := true +var _hide_single_tab := false + + +func _ready() -> void: + drag_to_rearrange_enabled = true + + +func _enter_tree() -> void: + active_tab_rearranged.connect(_on_tab_changed) + tab_selected.connect(_on_tab_selected) + tab_changed.connect(_on_tab_changed) + + +func _exit_tree() -> void: + active_tab_rearranged.disconnect(_on_tab_changed) + tab_selected.disconnect(_on_tab_selected) + tab_changed.disconnect(_on_tab_changed) + + +func track_nodes(nodes: Array[Control], new_leaf: DockableLayoutPanel) -> void: + _leaf = null # avoid using previous leaf in tab_changed signals + var min_size := mini(nodes.size(), get_child_count()) + # remove spare children + for i in range(min_size, get_child_count()): + var child := get_child(min_size) as DockableReferenceControl + child.reference_to = null + remove_child(child) + child.queue_free() + # add missing children + for i in range(min_size, nodes.size()): + var ref_control := DockableReferenceControl.new() + add_child(ref_control) + assert(nodes.size() == get_child_count(), "FIXME") + # setup children + for i in nodes.size(): + var ref_control := get_child(i) as DockableReferenceControl + ref_control.reference_to = nodes[i] + set_tab_title(i, nodes[i].name) + set_leaf(new_leaf) + _handle_tab_visibility() + + +func get_child_rect() -> Rect2: + var control := get_current_tab_control() + return Rect2(position + control.position, control.size) + + +func set_leaf(value: DockableLayoutPanel) -> void: + if get_tab_count() > 0 and value: + current_tab = clampi(value.current_tab, 0, get_tab_count() - 1) + _leaf = value + + +func get_leaf() -> DockableLayoutPanel: + return _leaf + + +func get_layout_minimum_size() -> Vector2: + return get_combined_minimum_size() + + +func _on_tab_selected(tab: int) -> void: + if _leaf: + _leaf.current_tab = tab + + +func _on_tab_changed(tab: int) -> void: + if not _leaf: + return + var control := get_tab_control(tab) + if not control: + return + var tab_name := control.name + var name_index_in_leaf := _leaf.find_name(tab_name) + if name_index_in_leaf != tab: # NOTE: this handles added tabs (index == -1) + tab_layout_changed.emit(tab) + + +func _handle_tab_visibility() -> void: + if _hide_single_tab and get_tab_count() == 1: + tabs_visible = false + else: + tabs_visible = _show_tabs diff --git a/godot/addons/dockable_container/dockable_panel_reference_control.gd b/godot/addons/dockable_container/dockable_panel_reference_control.gd new file mode 100644 index 0000000..06dc11b --- /dev/null +++ b/godot/addons/dockable_container/dockable_panel_reference_control.gd @@ -0,0 +1,49 @@ +@tool +class_name DockableReferenceControl +extends Container +## Control that mimics its own visibility and rect into another Control. + +var reference_to: Control: + get: + return _reference_to + set(control): + if _reference_to != control: + if is_instance_valid(_reference_to): + _reference_to.renamed.disconnect(_on_reference_to_renamed) + _reference_to.minimum_size_changed.disconnect(update_minimum_size) + _reference_to = control + + minimum_size_changed.emit() + if not is_instance_valid(_reference_to): + return + _reference_to.renamed.connect(_on_reference_to_renamed) + _reference_to.minimum_size_changed.connect(update_minimum_size) + _reference_to.visible = visible + _reposition_reference() + +var _reference_to: Control = null + + +func _ready() -> void: + mouse_filter = MOUSE_FILTER_IGNORE + set_notify_transform(true) + + +func _notification(what: int) -> void: + if what == NOTIFICATION_VISIBILITY_CHANGED and _reference_to: + _reference_to.visible = visible + elif what == NOTIFICATION_TRANSFORM_CHANGED and _reference_to: + _reposition_reference() + + +func _get_minimum_size() -> Vector2: + return _reference_to.get_combined_minimum_size() if _reference_to else Vector2.ZERO + + +func _reposition_reference() -> void: + _reference_to.global_position = global_position + _reference_to.size = size + + +func _on_reference_to_renamed() -> void: + name = _reference_to.name diff --git a/godot/addons/dockable_container/drag_n_drop_panel.gd b/godot/addons/dockable_container/drag_n_drop_panel.gd new file mode 100644 index 0000000..7e5d771 --- /dev/null +++ b/godot/addons/dockable_container/drag_n_drop_panel.gd @@ -0,0 +1,82 @@ +@tool +extends Control + +enum { MARGIN_LEFT, MARGIN_RIGHT, MARGIN_TOP, MARGIN_BOTTOM, MARGIN_CENTER } + +const DRAW_NOTHING := -1 +const DRAW_CENTERED := -2 +const MARGIN_NONE := -1 + +var _draw_margin := DRAW_NOTHING +var _should_split := false + + +func _notification(what: int) -> void: + if what == NOTIFICATION_MOUSE_EXIT: + _draw_margin = DRAW_NOTHING + queue_redraw() + elif what == NOTIFICATION_MOUSE_ENTER and not _should_split: + _draw_margin = DRAW_CENTERED + queue_redraw() + + +func _gui_input(event: InputEvent) -> void: + if _should_split and event is InputEventMouseMotion: + _draw_margin = _find_hover_margin(event.position) + queue_redraw() + + +func _draw() -> void: + var rect: Rect2 + if _draw_margin == DRAW_NOTHING: + return + elif _draw_margin == DRAW_CENTERED: + rect = Rect2(Vector2.ZERO, size) + elif _draw_margin == MARGIN_LEFT: + rect = Rect2(0, 0, size.x * 0.5, size.y) + elif _draw_margin == MARGIN_TOP: + rect = Rect2(0, 0, size.x, size.y * 0.5) + elif _draw_margin == MARGIN_RIGHT: + var half_width = size.x * 0.5 + rect = Rect2(half_width, 0, half_width, size.y) + elif _draw_margin == MARGIN_BOTTOM: + var half_height = size.y * 0.5 + rect = Rect2(0, half_height, size.x, half_height) + var stylebox := get_theme_stylebox("panel", "TooltipPanel") + draw_style_box(stylebox, rect) + + +func set_enabled(enabled: bool, should_split: bool = true) -> void: + visible = enabled + _should_split = should_split + if enabled: + _draw_margin = DRAW_NOTHING + queue_redraw() + + +func get_hover_margin() -> int: + return _draw_margin + + +func _find_hover_margin(point: Vector2) -> int: + var half_size := size * 0.5 + + var left := point.distance_squared_to(Vector2(0, half_size.y)) + var lesser := left + var lesser_margin := MARGIN_LEFT + + var top := point.distance_squared_to(Vector2(half_size.x, 0)) + if lesser > top: + lesser = top + lesser_margin = MARGIN_TOP + + var right := point.distance_squared_to(Vector2(size.x, half_size.y)) + if lesser > right: + lesser = right + lesser_margin = MARGIN_RIGHT + + var bottom := point.distance_squared_to(Vector2(half_size.x, size.y)) + if lesser > bottom: + #lesser = bottom # unused result + lesser_margin = MARGIN_BOTTOM + return lesser_margin diff --git a/godot/addons/dockable_container/icon.svg b/godot/addons/dockable_container/icon.svg new file mode 100644 index 0000000..d87d598 --- /dev/null +++ b/godot/addons/dockable_container/icon.svg @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/godot/addons/dockable_container/icon.svg.import b/godot/addons/dockable_container/icon.svg.import new file mode 100644 index 0000000..595e573 --- /dev/null +++ b/godot/addons/dockable_container/icon.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dy25danh2am23" +path="res://.godot/imported/icon.svg-35635e7bbda4487d4b2942da1d987df8.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/dockable_container/icon.svg" +dest_files=["res://.godot/imported/icon.svg-35635e7bbda4487d4b2942da1d987df8.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/godot/addons/dockable_container/inspector_plugin/editor_inspector_plugin.gd b/godot/addons/dockable_container/inspector_plugin/editor_inspector_plugin.gd new file mode 100644 index 0000000..73d0372 --- /dev/null +++ b/godot/addons/dockable_container/inspector_plugin/editor_inspector_plugin.gd @@ -0,0 +1,22 @@ +extends EditorInspectorPlugin + +const LayoutEditorProperty := preload("layout_editor_property.gd") + + +func _can_handle(object: Object) -> bool: + return object is DockableContainer + + +func _parse_property( + _object: Object, + _type: Variant.Type, + name: String, + _hint: PropertyHint, + _hint_text: String, + _usage: int, + _wide: bool +) -> bool: + if name == "layout": + var editor_property := LayoutEditorProperty.new() + add_property_editor("layout", editor_property) + return false diff --git a/godot/addons/dockable_container/inspector_plugin/layout_editor_property.gd b/godot/addons/dockable_container/inspector_plugin/layout_editor_property.gd new file mode 100644 index 0000000..eb00134 --- /dev/null +++ b/godot/addons/dockable_container/inspector_plugin/layout_editor_property.gd @@ -0,0 +1,71 @@ +extends EditorProperty + +var _container := DockableContainer.new() +var _hidden_menu_button := MenuButton.new() +var _hidden_menu_popup: PopupMenu +var _hidden_menu_list: PackedStringArray + + +func _ready() -> void: + custom_minimum_size = Vector2(128, 256) + + _hidden_menu_button.text = "Visible nodes" + add_child(_hidden_menu_button) + _hidden_menu_popup = _hidden_menu_button.get_popup() + _hidden_menu_popup.hide_on_checkable_item_selection = false + _hidden_menu_popup.about_to_popup.connect(_on_hidden_menu_popup_about_to_show) + _hidden_menu_popup.id_pressed.connect(_on_hidden_menu_popup_id_pressed) + + _container.clone_layout_on_ready = false + _container.custom_minimum_size = custom_minimum_size + + var value := _get_layout().clone() # The layout gets reset when selecting it without clone + for n in value.get_names(): + var child := _create_child_control(n) + _container.add_child(child) + _container.set(get_edited_property(), value) + add_child(_container) + set_bottom_editor(_container) + + +func _exit_tree() -> void: # Not sure if this is needed, but just to be sure + queue_free() + + +func _update_property() -> void: + var value := _get_layout() + _container.set(get_edited_property(), value) + + +func _get_layout() -> DockableLayout: + var original_container := get_edited_object() as DockableContainer + return original_container.get(get_edited_property()) + + +func _create_child_control(named: String) -> Label: + var new_control := Label.new() + new_control.name = named + new_control.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER + new_control.vertical_alignment = VERTICAL_ALIGNMENT_CENTER + new_control.clip_text = true + new_control.text = named + return new_control + + +func _on_hidden_menu_popup_about_to_show() -> void: + var layout := _get_layout().clone() + _hidden_menu_popup.clear() + _hidden_menu_list = layout.get_names() + for i in _hidden_menu_list.size(): + var tab_name := _hidden_menu_list[i] + _hidden_menu_popup.add_check_item(tab_name, i) + _hidden_menu_popup.set_item_checked(i, not layout.is_tab_hidden(tab_name)) + + +func _on_hidden_menu_popup_id_pressed(id: int) -> void: + var layout := _get_layout().clone() + var tab_name := _hidden_menu_list[id] + var new_hidden := not layout.is_tab_hidden(tab_name) + _get_layout().set_tab_hidden(tab_name, new_hidden) + _hidden_menu_popup.set_item_checked(id, not new_hidden) + emit_changed(get_edited_property(), _get_layout()) # This line may not be needed diff --git a/godot/addons/dockable_container/layout.gd b/godot/addons/dockable_container/layout.gd new file mode 100644 index 0000000..e2a8036 --- /dev/null +++ b/godot/addons/dockable_container/layout.gd @@ -0,0 +1,242 @@ +@tool +class_name DockableLayout +extends Resource +## DockableLayout Resource definition, holding the root DockableLayoutNode and hidden tabs. +## +## DockableLayoutSplit are binary trees with nested DockableLayoutSplit subtrees +## and DockableLayoutPanel leaves. Both of them inherit from DockableLayoutNode to help with +## type annotation and define common functionality. +## +## Hidden tabs are marked in the `hidden_tabs` Dictionary by name. + +enum { MARGIN_LEFT, MARGIN_RIGHT, MARGIN_TOP, MARGIN_BOTTOM, MARGIN_CENTER } + +@export var root: DockableLayoutNode = DockableLayoutPanel.new(): + get: + return _root + set(value): + set_root(value) +@export var hidden_tabs := {}: + get: + return _hidden_tabs + set(value): + if value != _hidden_tabs: + _hidden_tabs = value + changed.emit() + +var _changed_signal_queued := false +var _first_leaf: DockableLayoutPanel +var _hidden_tabs: Dictionary +var _leaf_by_node_name: Dictionary +var _root: DockableLayoutNode = DockableLayoutPanel.new() + + +func _init() -> void: + resource_name = "Layout" + + +func set_root(value: DockableLayoutNode, should_emit_changed := true) -> void: + if not value: + value = DockableLayoutPanel.new() + if _root == value: + return + if _root and _root.changed.is_connected(_on_root_changed): + _root.changed.disconnect(_on_root_changed) + _root = value + _root.parent = null + _root.changed.connect(_on_root_changed) + if should_emit_changed: + _on_root_changed() + + +func get_root() -> DockableLayoutNode: + return _root + + +func clone() -> DockableLayout: + return duplicate(true) + + +func get_names() -> PackedStringArray: + return _root.get_names() + + +## Add missing nodes on first leaf and remove nodes outside indices from leaves. +## +## _leaf_by_node_name = { +## (string keys) = respective Leaf that holds the node name, +## } +func update_nodes(names: PackedStringArray) -> void: + _leaf_by_node_name.clear() + _first_leaf = null + var empty_leaves: Array[DockableLayoutPanel] = [] + _ensure_names_in_node(_root, names, empty_leaves) # Changes _leaf_by_node_name and empty_leaves + for l in empty_leaves: + _remove_leaf(l) + if not _first_leaf: + _first_leaf = DockableLayoutPanel.new() + set_root(_first_leaf) + for n in names: + if not _leaf_by_node_name.has(n): + _first_leaf.push_name(n) + _leaf_by_node_name[n] = _first_leaf + _on_root_changed() + + +func move_node_to_leaf(node: Node, leaf: DockableLayoutPanel, relative_position: int) -> void: + var node_name := node.name + var previous_leaf: DockableLayoutPanel = _leaf_by_node_name.get(node_name) + if previous_leaf: + previous_leaf.remove_node(node) + if previous_leaf.is_empty(): + _remove_leaf(previous_leaf) + + leaf.insert_node(relative_position, node) + _leaf_by_node_name[node_name] = leaf + _on_root_changed() + + +func get_leaf_for_node(node: Node) -> DockableLayoutPanel: + return _leaf_by_node_name.get(node.name) + + +func split_leaf_with_node(leaf: DockableLayoutPanel, node: Node, margin: int) -> void: + var root_branch := leaf.parent + var new_leaf := DockableLayoutPanel.new() + var new_branch := DockableLayoutSplit.new() + if margin == MARGIN_LEFT or margin == MARGIN_RIGHT: + new_branch.direction = DockableLayoutSplit.Direction.HORIZONTAL + else: + new_branch.direction = DockableLayoutSplit.Direction.VERTICAL + if margin == MARGIN_LEFT or margin == MARGIN_TOP: + new_branch.first = new_leaf + new_branch.second = leaf + else: + new_branch.first = leaf + new_branch.second = new_leaf + if _root == leaf: + set_root(new_branch, false) + elif root_branch: + if leaf == root_branch.first: + root_branch.first = new_branch + else: + root_branch.second = new_branch + + move_node_to_leaf(node, new_leaf, 0) + + +func add_node(node: Node) -> void: + var node_name := node.name + if _leaf_by_node_name.has(node_name): + return + _first_leaf.push_name(node_name) + _leaf_by_node_name[node_name] = _first_leaf + _on_root_changed() + + +func remove_node(node: Node) -> void: + var node_name := node.name + var leaf: DockableLayoutPanel = _leaf_by_node_name.get(node_name) + if not leaf: + return + leaf.remove_node(node) + _leaf_by_node_name.erase(node_name) + if leaf.is_empty(): + _remove_leaf(leaf) + _on_root_changed() + + +func rename_node(previous_name: String, new_name: String) -> void: + var leaf: DockableLayoutPanel = _leaf_by_node_name.get(previous_name) + if not leaf: + return + leaf.rename_node(previous_name, new_name) + _leaf_by_node_name.erase(previous_name) + _leaf_by_node_name[new_name] = leaf + _on_root_changed() + + +func set_tab_hidden(name: String, hidden: bool) -> void: + if not _leaf_by_node_name.has(name): + return + if hidden: + _hidden_tabs[name] = true + else: + _hidden_tabs.erase(name) + _on_root_changed() + + +func is_tab_hidden(name: String) -> bool: + return _hidden_tabs.get(name, false) + + +func set_node_hidden(node: Node, hidden: bool) -> void: + set_tab_hidden(node.name, hidden) + + +func is_node_hidden(node: Node) -> bool: + return is_tab_hidden(node.name) + + +func _on_root_changed() -> void: + if _changed_signal_queued: + return + _changed_signal_queued = true + set_deferred("_changed_signal_queued", false) + emit_changed.call_deferred() + + +func _ensure_names_in_node( + node: DockableLayoutNode, names: PackedStringArray, empty_leaves: Array[DockableLayoutPanel] +) -> void: + if node is DockableLayoutPanel: + node.update_nodes(names, _leaf_by_node_name) # This changes _leaf_by_node_name + if node.is_empty(): + empty_leaves.append(node) + if not _first_leaf: + _first_leaf = node + elif node is DockableLayoutSplit: + _ensure_names_in_node(node.first, names, empty_leaves) + _ensure_names_in_node(node.second, names, empty_leaves) + else: + assert(false, "Invalid Resource, should be branch or leaf, found %s" % node) + + +func _remove_leaf(leaf: DockableLayoutPanel) -> void: + assert(leaf.is_empty(), "FIXME: trying to remove_at a leaf with nodes") + if _root == leaf: + return + var collapsed_branch := leaf.parent + assert(collapsed_branch is DockableLayoutSplit, "FIXME: leaf is not a child of branch") + var kept_branch: DockableLayoutNode = ( + collapsed_branch.first if leaf == collapsed_branch.second else collapsed_branch.second + ) + var root_branch := collapsed_branch.parent #HERE + if collapsed_branch == _root: + set_root(kept_branch, true) + elif root_branch: + if collapsed_branch == root_branch.first: + root_branch.first = kept_branch + else: + root_branch.second = kept_branch + + +func _print_tree() -> void: + print("TREE") + _print_tree_step(_root, 0, 0) + print("") + + +func _print_tree_step(tree_or_leaf: DockableLayoutNode, level: int, idx: int) -> void: + if tree_or_leaf is DockableLayoutPanel: + print(" |".repeat(level), "- (%d) = " % idx, tree_or_leaf.names) + elif tree_or_leaf is DockableLayoutSplit: + print( + " |".repeat(level), + "-+ (%d) = " % idx, + tree_or_leaf.direction, + " ", + tree_or_leaf.percent + ) + _print_tree_step(tree_or_leaf.first, level + 1, 1) + _print_tree_step(tree_or_leaf.second, level + 1, 2) diff --git a/godot/addons/dockable_container/layout_node.gd b/godot/addons/dockable_container/layout_node.gd new file mode 100644 index 0000000..ba3accb --- /dev/null +++ b/godot/addons/dockable_container/layout_node.gd @@ -0,0 +1,29 @@ +@tool +class_name DockableLayoutNode +extends Resource +## Base class for DockableLayout tree nodes + +var parent: DockableLayoutSplit: + get: + return _parent_ref.get_ref() + set(value): + _parent_ref = weakref(value) + +var _parent_ref := WeakRef.new() + + +func emit_tree_changed() -> void: + var node := self + while node: + node.emit_changed() + node = node.parent + + +## Returns whether there are any nodes +func is_empty() -> bool: + return true + + +## Returns all tab names in this node +func get_names() -> PackedStringArray: + return PackedStringArray() diff --git a/godot/addons/dockable_container/layout_panel.gd b/godot/addons/dockable_container/layout_panel.gd new file mode 100644 index 0000000..e15201b --- /dev/null +++ b/godot/addons/dockable_container/layout_panel.gd @@ -0,0 +1,89 @@ +@tool +class_name DockableLayoutPanel +extends DockableLayoutNode +## DockableLayout leaf nodes, defining tabs + +@export var names: PackedStringArray: + get: + return get_names() + set(value): + _names = value + emit_tree_changed() +@export var current_tab: int: + get: + return int(clamp(_current_tab, 0, _names.size() - 1)) + set(value): + if value != _current_tab: + _current_tab = value + emit_tree_changed() + +var _names := PackedStringArray() +var _current_tab := 0 + + +func _init() -> void: + resource_name = "Tabs" + + +## Returns all tab names in this node +func get_names() -> PackedStringArray: + return _names + + +func push_name(name: String) -> void: + _names.append(name) + emit_tree_changed() + + +func insert_node(position: int, node: Node) -> void: + _names.insert(position, node.name) + emit_tree_changed() + + +func find_name(node_name: String) -> int: + for i in _names.size(): + if _names[i] == node_name: + return i + return -1 + + +func find_child(node: Node) -> int: + return find_name(node.name) + + +func remove_node(node: Node) -> void: + var i := find_child(node) + if i >= 0: + _names.remove_at(i) + emit_tree_changed() + else: + push_warning("Remove failed, node '%s' was not found" % node) + + +func rename_node(previous_name: String, new_name: String) -> void: + var i := find_name(previous_name) + if i >= 0: + _names.set(i, new_name) + emit_tree_changed() + else: + push_warning("Rename failed, name '%s' was not found" % previous_name) + + +## Returns whether there are any nodes +func is_empty() -> bool: + return _names.is_empty() + + +func update_nodes(node_names: PackedStringArray, data: Dictionary) -> void: + var i := 0 + var removed_any := false + while i < _names.size(): + var current := _names[i] + if not current in node_names or data.has(current): + _names.remove_at(i) + removed_any = true + else: + data[current] = self + i += 1 + if removed_any: + emit_tree_changed() diff --git a/godot/addons/dockable_container/layout_split.gd b/godot/addons/dockable_container/layout_split.gd new file mode 100644 index 0000000..5e78138 --- /dev/null +++ b/godot/addons/dockable_container/layout_split.gd @@ -0,0 +1,100 @@ +@tool +class_name DockableLayoutSplit +extends DockableLayoutNode +## DockableLayout binary tree nodes, defining subtrees and leaf panels + +enum Direction { HORIZONTAL, VERTICAL } + +@export var direction := Direction.HORIZONTAL: + get: + return get_direction() + set(value): + set_direction(value) +@export_range(0, 1) var percent := 0.5: + get = get_percent, + set = set_percent +@export var first: DockableLayoutNode = DockableLayoutPanel.new(): + get: + return get_first() + set(value): + set_first(value) +@export var second: DockableLayoutNode = DockableLayoutPanel.new(): + get: + return get_second() + set(value): + set_second(value) + +var _direction := Direction.HORIZONTAL +var _percent := 0.5 +var _first: DockableLayoutNode +var _second: DockableLayoutNode + + +func _init() -> void: + resource_name = "Split" + + +func set_first(value: DockableLayoutNode) -> void: + if value == null: + _first = DockableLayoutPanel.new() + else: + _first = value + _first.parent = self + emit_tree_changed() + + +func get_first() -> DockableLayoutNode: + return _first + + +func set_second(value: DockableLayoutNode) -> void: + if value == null: + _second = DockableLayoutPanel.new() + else: + _second = value + _second.parent = self + emit_tree_changed() + + +func get_second() -> DockableLayoutNode: + return _second + + +func set_direction(value: Direction) -> void: + if value != _direction: + _direction = value + emit_tree_changed() + + +func get_direction() -> Direction: + return _direction + + +func set_percent(value: float) -> void: + var clamped_value := clampf(value, 0, 1) + if not is_equal_approx(_percent, clamped_value): + _percent = clamped_value + emit_tree_changed() + + +func get_percent() -> float: + return _percent + + +func get_names() -> PackedStringArray: + var names := _first.get_names() + names.append_array(_second.get_names()) + return names + + +## Returns whether there are any nodes +func is_empty() -> bool: + return _first.is_empty() and _second.is_empty() + + +func is_horizontal() -> bool: + return _direction == Direction.HORIZONTAL + + +func is_vertical() -> bool: + return _direction == Direction.VERTICAL diff --git a/godot/addons/dockable_container/plugin.cfg b/godot/addons/dockable_container/plugin.cfg new file mode 100644 index 0000000..b359591 --- /dev/null +++ b/godot/addons/dockable_container/plugin.cfg @@ -0,0 +1,13 @@ +[plugin] + +name="Dockable Container" +description="Container script that manages docking/tiling UI panels. + +Panels are composed of tabs that can be dragged around and dropped to split another panel or compose its tabs. + +Layout information is stored in Resource objects, so they can be saved/loaded from disk easily. + +This plugin also offers a replica of the Container layout to be edited directly in the inspector." +author="gilzoide" +version="1.1.2" +script="plugin.gd" diff --git a/godot/addons/dockable_container/plugin.gd b/godot/addons/dockable_container/plugin.gd new file mode 100644 index 0000000..e93e010 --- /dev/null +++ b/godot/addons/dockable_container/plugin.gd @@ -0,0 +1,19 @@ +@tool +extends EditorPlugin + +const LayoutInspectorPlugin := preload("inspector_plugin/editor_inspector_plugin.gd") +const Icon := preload("icon.svg") + +var _layout_inspector_plugin: LayoutInspectorPlugin + + +func _enter_tree() -> void: + _layout_inspector_plugin = LayoutInspectorPlugin.new() + add_custom_type("DockableContainer", "Container", DockableContainer, Icon) + add_inspector_plugin(_layout_inspector_plugin) + + +func _exit_tree() -> void: + remove_inspector_plugin(_layout_inspector_plugin) + remove_custom_type("DockableContainer") + _layout_inspector_plugin = null diff --git a/godot/addons/dockable_container/samples/TestScene.gd b/godot/addons/dockable_container/samples/TestScene.gd new file mode 100644 index 0000000..f94ac97 --- /dev/null +++ b/godot/addons/dockable_container/samples/TestScene.gd @@ -0,0 +1,63 @@ +extends VBoxContainer + +const SAVED_LAYOUT_PATH := "user://layout.tres" + +@onready var _container := $DockableContainers/DockableContainer as DockableContainer +@onready var _clone_control := $HBoxContainer/ControlPrefab as ColorRect +@onready var _checkbox_container := $HBoxContainer as HBoxContainer + + +func _ready() -> void: + if not OS.is_userfs_persistent(): + $HBoxContainer/SaveLayoutButton.visible = false + $HBoxContainer/LoadLayoutButton.visible = false + + var tabs := _container.get_tabs() + for i in tabs.size(): + var checkbox := CheckBox.new() + checkbox.text = str(i) + checkbox.button_pressed = not _container.is_control_hidden(tabs[i]) + checkbox.toggled.connect(_on_CheckButton_toggled.bind(tabs[i])) + _checkbox_container.add_child(checkbox) + + +func _on_add_pressed() -> void: + var control := _clone_control.duplicate() + control.get_node("Buttons/Rename").pressed.connect( + _on_control_rename_button_pressed.bind(control) + ) + control.get_node("Buttons/Remove").pressed.connect( + _on_control_remove_button_pressed.bind(control) + ) + control.color = Color(randf(), randf(), randf()) + control.name = "Control0" + + _container.add_child(control, true) + await _container.sort_children + _container.set_control_as_current_tab(control) + + +func _on_save_pressed() -> void: + if ResourceSaver.save(_container.layout, SAVED_LAYOUT_PATH) != OK: + print("ERROR") + + +func _on_load_pressed() -> void: + var res = load(SAVED_LAYOUT_PATH) + if res: + _container.set_layout(res.clone()) + else: + print("Error") + + +func _on_control_rename_button_pressed(control: Control) -> void: + control.name = StringName(str(control.name) + " =D") + + +func _on_control_remove_button_pressed(control: Control) -> void: + control.get_parent().remove_child(control) + control.queue_free() + + +func _on_CheckButton_toggled(button_pressed: bool, tab: Control) -> void: + _container.set_control_hidden(tab, not button_pressed) diff --git a/godot/addons/dockable_container/samples/TestScene.tscn b/godot/addons/dockable_container/samples/TestScene.tscn new file mode 100644 index 0000000..80ca9cc --- /dev/null +++ b/godot/addons/dockable_container/samples/TestScene.tscn @@ -0,0 +1,177 @@ +[gd_scene load_steps=16 format=3 uid="uid://drlvhuchtk6if"] + +[ext_resource type="Script" path="res://addons/dockable_container/dockable_container.gd" id="1"] +[ext_resource type="Script" path="res://addons/dockable_container/layout.gd" id="2"] +[ext_resource type="Script" path="res://addons/dockable_container/samples/TestScene.gd" id="4"] +[ext_resource type="Script" path="res://addons/dockable_container/layout_split.gd" id="4_yhgfb"] +[ext_resource type="Script" path="res://addons/dockable_container/layout_panel.gd" id="5"] + +[sub_resource type="Resource" id="Resource_8aoc2"] +resource_name = "Tabs" +script = ExtResource("5") +names = PackedStringArray("Control0") +current_tab = 0 + +[sub_resource type="Resource" id="Resource_6kjom"] +resource_name = "Tabs" +script = ExtResource("5") +names = PackedStringArray("Control1", "Control2") +current_tab = 0 + +[sub_resource type="Resource" id="Resource_hl8y1"] +resource_name = "Split" +script = ExtResource("4_yhgfb") +direction = 1 +percent = 0.5 +first = SubResource("Resource_8aoc2") +second = SubResource("Resource_6kjom") + +[sub_resource type="Resource" id="Resource_ybwqe"] +resource_name = "Layout" +script = ExtResource("2") +root = SubResource("Resource_hl8y1") +hidden_tabs = {} + +[sub_resource type="Resource" id="Resource_ntwfj"] +resource_name = "Tabs" +script = ExtResource("5") +names = PackedStringArray("Control3") +current_tab = 0 + +[sub_resource type="Resource" id="Resource_dmyvf"] +resource_name = "Tabs" +script = ExtResource("5") +names = PackedStringArray("Control4") +current_tab = 0 + +[sub_resource type="Resource" id="Resource_vag66"] +resource_name = "Split" +script = ExtResource("4_yhgfb") +direction = 1 +percent = 0.281 +first = SubResource("Resource_ntwfj") +second = SubResource("Resource_dmyvf") + +[sub_resource type="Resource" id="Resource_4q660"] +resource_name = "Tabs" +script = ExtResource("5") +names = PackedStringArray("Control5") +current_tab = 0 + +[sub_resource type="Resource" id="Resource_jhibs"] +resource_name = "Split" +script = ExtResource("4_yhgfb") +direction = 0 +percent = 0.5 +first = SubResource("Resource_vag66") +second = SubResource("Resource_4q660") + +[sub_resource type="Resource" id="Resource_xhxpg"] +resource_name = "Layout" +script = ExtResource("2") +root = SubResource("Resource_jhibs") +hidden_tabs = {} + +[node name="SampleScene" type="VBoxContainer"] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +script = ExtResource("4") + +[node name="HBoxContainer" type="HBoxContainer" parent="."] +layout_mode = 2 +alignment = 1 + +[node name="AddControlButton" type="Button" parent="HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 0 +size_flags_vertical = 4 +text = "(+) ADD CONTROL" + +[node name="SaveLayoutButton" type="Button" parent="HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 0 +size_flags_vertical = 4 +text = "Save Layout" + +[node name="LoadLayoutButton" type="Button" parent="HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 0 +size_flags_vertical = 4 +text = "Load Layout" + +[node name="ControlPrefab" type="ColorRect" parent="HBoxContainer"] +visible = false +layout_mode = 2 +color = Color(0.129412, 0.121569, 0.121569, 1) + +[node name="Buttons" type="VBoxContainer" parent="HBoxContainer/ControlPrefab"] +layout_mode = 0 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -65.5 +offset_top = -22.0 +offset_right = 65.5 +offset_bottom = 22.0 + +[node name="Rename" type="Button" parent="HBoxContainer/ControlPrefab/Buttons"] +layout_mode = 2 +text = "Rename" + +[node name="Remove" type="Button" parent="HBoxContainer/ControlPrefab/Buttons"] +layout_mode = 2 +text = "REMOVE" + +[node name="DockableContainers" type="HBoxContainer" parent="."] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="DockableContainer" type="Container" parent="DockableContainers"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +script = ExtResource("1") +layout = SubResource("Resource_ybwqe") + +[node name="Control0" type="ColorRect" parent="DockableContainers/DockableContainer"] +layout_mode = 2 + +[node name="Control1" type="ColorRect" parent="DockableContainers/DockableContainer"] +layout_mode = 2 +color = Color(0.141176, 0.0745098, 0.603922, 1) + +[node name="Control2" type="ColorRect" parent="DockableContainers/DockableContainer"] +visible = false +layout_mode = 2 +color = Color(0.533333, 0.380392, 0.380392, 1) + +[node name="Separator" type="ColorRect" parent="DockableContainers"] +custom_minimum_size = Vector2(50, 0) +layout_mode = 2 +color = Color(0, 0, 0, 1) + +[node name="DockableContainer2" type="Container" parent="DockableContainers"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +script = ExtResource("1") +layout = SubResource("Resource_xhxpg") + +[node name="Control3" type="ColorRect" parent="DockableContainers/DockableContainer2"] +layout_mode = 2 +color = Color(0, 1, 0.905882, 1) + +[node name="Control4" type="ColorRect" parent="DockableContainers/DockableContainer2"] +layout_mode = 2 +color = Color(0, 0.698039, 0.0588235, 1) + +[node name="Control5" type="ColorRect" parent="DockableContainers/DockableContainer2"] +layout_mode = 2 +color = Color(1, 0.937255, 0, 1) + +[connection signal="pressed" from="HBoxContainer/AddControlButton" to="." method="_on_add_pressed"] +[connection signal="pressed" from="HBoxContainer/SaveLayoutButton" to="." method="_on_save_pressed"] +[connection signal="pressed" from="HBoxContainer/LoadLayoutButton" to="." method="_on_load_pressed"] diff --git a/godot/addons/dockable_container/split_handle.gd b/godot/addons/dockable_container/split_handle.gd new file mode 100644 index 0000000..baf4b1f --- /dev/null +++ b/godot/addons/dockable_container/split_handle.gd @@ -0,0 +1,120 @@ +@tool +extends Control + +const SPLIT_THEME_CLASS: PackedStringArray = [ + "HSplitContainer", # SPLIT_THEME_CLASS[DockableLayoutSplit.Direction.HORIZONTAL] + "VSplitContainer", # SPLIT_THEME_CLASS[DockableLayoutSplit.Direction.VERTICAL] +] + +const SPLIT_MOUSE_CURSOR_SHAPE: Array[Control.CursorShape] = [ + Control.CURSOR_HSPLIT, # SPLIT_MOUSE_CURSOR_SHAPE[DockableLayoutSplit.Direction.HORIZONTAL] + Control.CURSOR_VSPLIT, # SPLIT_MOUSE_CURSOR_SHAPE[DockableLayoutSplit.Direction.VERTICAL] +] + +var layout_split: DockableLayoutSplit +var first_minimum_size: Vector2 +var second_minimum_size: Vector2 + +var _parent_rect: Rect2 +var _mouse_hovering := false +var _dragging := false + + +func _draw() -> void: + var theme_class := SPLIT_THEME_CLASS[layout_split.direction] + var icon := get_theme_icon("grabber", theme_class) + var autohide := bool(get_theme_constant("autohide", theme_class)) + if not icon or (autohide and not _mouse_hovering): + return + + draw_texture(icon, (size - icon.get_size()) * 0.5) + + +func _gui_input(event: InputEvent) -> void: + if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT: + _dragging = event.is_pressed() + if event.double_click: + layout_split.percent = 0.5 + elif _dragging and event is InputEventMouseMotion: + var mouse_in_parent := get_parent_control().get_local_mouse_position() + if layout_split.is_horizontal(): + layout_split.percent = ( + (mouse_in_parent.x - _parent_rect.position.x) / _parent_rect.size.x + ) + else: + layout_split.percent = ( + (mouse_in_parent.y - _parent_rect.position.y) / _parent_rect.size.y + ) + + +func _notification(what: int) -> void: + if what == NOTIFICATION_MOUSE_ENTER: + _mouse_hovering = true + set_split_cursor(true) + if bool(get_theme_constant("autohide", SPLIT_THEME_CLASS[layout_split.direction])): + queue_redraw() + elif what == NOTIFICATION_MOUSE_EXIT: + _mouse_hovering = false + set_split_cursor(false) + if bool(get_theme_constant("autohide", SPLIT_THEME_CLASS[layout_split.direction])): + queue_redraw() + elif what == NOTIFICATION_FOCUS_EXIT: + _dragging = false + + +func get_layout_minimum_size() -> Vector2: + if not layout_split: + return Vector2.ZERO + var separation := get_theme_constant("separation", SPLIT_THEME_CLASS[layout_split.direction]) + if layout_split.is_horizontal(): + return Vector2( + first_minimum_size.x + separation + second_minimum_size.x, + maxf(first_minimum_size.y, second_minimum_size.y) + ) + else: + return Vector2( + maxf(first_minimum_size.x, second_minimum_size.x), + first_minimum_size.y + separation + second_minimum_size.y + ) + + +func set_split_cursor(value: bool) -> void: + if value: + mouse_default_cursor_shape = SPLIT_MOUSE_CURSOR_SHAPE[layout_split.direction] + else: + mouse_default_cursor_shape = CURSOR_ARROW + + +func get_split_rects(rect: Rect2) -> Dictionary: + _parent_rect = rect + var separation := get_theme_constant("separation", SPLIT_THEME_CLASS[layout_split.direction]) + var origin := rect.position + var percent := layout_split.percent + if layout_split.is_horizontal(): + var split_offset := clampf( + rect.size.x * percent - separation * 0.5, + first_minimum_size.x, + rect.size.x - second_minimum_size.x - separation + ) + var second_width := rect.size.x - split_offset - separation + + return { + "first": Rect2(origin.x, origin.y, split_offset, rect.size.y), + "self": Rect2(origin.x + split_offset, origin.y, separation, rect.size.y), + "second": + Rect2(origin.x + split_offset + separation, origin.y, second_width, rect.size.y), + } + else: + var split_offset := clampf( + rect.size.y * percent - separation * 0.5, + first_minimum_size.y, + rect.size.y - second_minimum_size.y - separation + ) + var second_height := rect.size.y - split_offset - separation + + return { + "first": Rect2(origin.x, origin.y, rect.size.x, split_offset), + "self": Rect2(origin.x, origin.y + split_offset, rect.size.x, separation), + "second": + Rect2(origin.x, origin.y + split_offset + separation, rect.size.x, second_height), + } diff --git a/godot/addons/uuid/uuid.gd b/godot/addons/uuid/uuid.gd new file mode 100644 index 0000000..a95aab1 --- /dev/null +++ b/godot/addons/uuid/uuid.gd @@ -0,0 +1,113 @@ +# Note: The code might not be as pretty it could be, since it's written +# in a way that maximizes performance. Methods are inlined and loops are avoided. +extends Node + +const BYTE_MASK: int = 0b11111111 + +static func uuidbin(): + # 16 random bytes with the bytes on index 6 and 8 modified + return [ + randi() & BYTE_MASK, randi() & BYTE_MASK, randi() & BYTE_MASK, randi() & BYTE_MASK, + randi() & BYTE_MASK, randi() & BYTE_MASK, ((randi() & BYTE_MASK) & 0x0f) | 0x40, randi() & BYTE_MASK, + ((randi() & BYTE_MASK) & 0x3f) | 0x80, randi() & BYTE_MASK, randi() & BYTE_MASK, randi() & BYTE_MASK, + randi() & BYTE_MASK, randi() & BYTE_MASK, randi() & BYTE_MASK, randi() & BYTE_MASK, + ] + +static func uuidbinrng(rng: RandomNumberGenerator): + return [ + rng.randi() & BYTE_MASK, rng.randi() & BYTE_MASK, rng.randi() & BYTE_MASK, rng.randi() & BYTE_MASK, + rng.randi() & BYTE_MASK, rng.randi() & BYTE_MASK, ((rng.randi() & BYTE_MASK) & 0x0f) | 0x40, rng.randi() & BYTE_MASK, + ((rng.randi() & BYTE_MASK) & 0x3f) | 0x80, rng.randi() & BYTE_MASK, rng.randi() & BYTE_MASK, rng.randi() & BYTE_MASK, + rng.randi() & BYTE_MASK, rng.randi() & BYTE_MASK, rng.randi() & BYTE_MASK, rng.randi() & BYTE_MASK, + ] + +static func v4(): + # 16 random bytes with the bytes on index 6 and 8 modified + var b = uuidbin() + + return '%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x' % [ + # low + b[0], b[1], b[2], b[3], + + # mid + b[4], b[5], + + # hi + b[6], b[7], + + # clock + b[8], b[9], + + # clock + b[10], b[11], b[12], b[13], b[14], b[15] + ] + +static func v4_rng(rng: RandomNumberGenerator): + # 16 random bytes with the bytes on index 6 and 8 modified + var b = uuidbinrng(rng) + + return '%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x' % [ + # low + b[0], b[1], b[2], b[3], + + # mid + b[4], b[5], + + # hi + b[6], b[7], + + # clock + b[8], b[9], + + # clock + b[10], b[11], b[12], b[13], b[14], b[15] + ] + +var _uuid: Array + +func _init(rng := RandomNumberGenerator.new()) -> void: + _uuid = uuidbinrng(rng) + +func as_array() -> Array: + return _uuid.duplicate() + +func as_dict(big_endian := true) -> Dictionary: + if big_endian: + return { + "low" : (_uuid[0] << 24) + (_uuid[1] << 16) + (_uuid[2] << 8 ) + _uuid[3], + "mid" : (_uuid[4] << 8 ) + _uuid[5], + "hi" : (_uuid[6] << 8 ) + _uuid[7], + "clock": (_uuid[8] << 8 ) + _uuid[9], + "node" : (_uuid[10] << 40) + (_uuid[11] << 32) + (_uuid[12] << 24) + (_uuid[13] << 16) + (_uuid[14] << 8 ) + _uuid[15] + } + else: + return { + "low" : _uuid[0] + (_uuid[1] << 8 ) + (_uuid[2] << 16) + (_uuid[3] << 24), + "mid" : _uuid[4] + (_uuid[5] << 8 ), + "hi" : _uuid[6] + (_uuid[7] << 8 ), + "clock": _uuid[8] + (_uuid[9] << 8 ), + "node" : _uuid[10] + (_uuid[11] << 8 ) + (_uuid[12] << 16) + (_uuid[13] << 24) + (_uuid[14] << 32) + (_uuid[15] << 40) + } + +func as_string() -> String: + return '%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x' % [ + # low + _uuid[0], _uuid[1], _uuid[2], _uuid[3], + + # mid + _uuid[4], _uuid[5], + + # hi + _uuid[6], _uuid[7], + + # clock + _uuid[8], _uuid[9], + + # node + _uuid[10], _uuid[11], _uuid[12], _uuid[13], _uuid[14], _uuid[15] + ] + +func is_equal(other) -> bool: + # Godot Engine compares Array recursively + # There's no need for custom comparison here. + return _uuid == other._uuid diff --git a/godot/export_presets.cfg b/godot/export_presets.cfg new file mode 100644 index 0000000..7396d57 --- /dev/null +++ b/godot/export_presets.cfg @@ -0,0 +1,64 @@ +[preset.0] + +name="Windows Desktop" +platform="Windows Desktop" +runnable=true +advanced_options=false +dedicated_server=false +custom_features="" +export_filter="all_resources" +include_filter="" +exclude_filter="" +export_path="../build/AquamarinePainter.exe" +encryption_include_filters="" +encryption_exclude_filters="" +encrypt_pck=false +encrypt_directory=false +script_export_mode=2 + +[preset.0.options] + +custom_template/debug="" +custom_template/release="" +debug/export_console_wrapper=0 +binary_format/embed_pck=true +texture_format/s3tc_bptc=true +texture_format/etc2_astc=false +binary_format/architecture="x86_64" +codesign/enable=false +codesign/timestamp=true +codesign/timestamp_server_url="" +codesign/digest_algorithm=1 +codesign/description="" +codesign/custom_options=PackedStringArray() +application/modify_resources=true +application/icon="res://icon.svg" +application/console_wrapper_icon="" +application/icon_interpolation=4 +application/file_version="" +application/product_version="" +application/company_name="Aquamarine Painter" +application/product_name="Aquamarine Painter" +application/file_description="Aquamarine Painter" +application/copyright="©2024 ORITO Itsuki" +application/trademarks="" +application/export_angle=0 +application/export_d3d12=0 +application/d3d12_agility_sdk_multiarch=true +ssh_remote_deploy/enabled=false +ssh_remote_deploy/host="user@host_ip" +ssh_remote_deploy/port="22" +ssh_remote_deploy/extra_args_ssh="" +ssh_remote_deploy/extra_args_scp="" +ssh_remote_deploy/run_script="Expand-Archive -LiteralPath '{temp_dir}\\{archive_name}' -DestinationPath '{temp_dir}' +$action = New-ScheduledTaskAction -Execute '{temp_dir}\\{exe_name}' -Argument '{cmd_args}' +$trigger = New-ScheduledTaskTrigger -Once -At 00:00 +$settings = New-ScheduledTaskSettingsSet +$task = New-ScheduledTask -Action $action -Trigger $trigger -Settings $settings +Register-ScheduledTask godot_remote_debug -InputObject $task -Force:$true +Start-ScheduledTask -TaskName godot_remote_debug +while (Get-ScheduledTask -TaskName godot_remote_debug | ? State -eq running) { Start-Sleep -Milliseconds 100 } +Unregister-ScheduledTask -TaskName godot_remote_debug -Confirm:$false -ErrorAction:SilentlyContinue" +ssh_remote_deploy/cleanup_script="Stop-ScheduledTask -TaskName godot_remote_debug -ErrorAction:SilentlyContinue +Unregister-ScheduledTask -TaskName godot_remote_debug -Confirm:$false -ErrorAction:SilentlyContinue +Remove-Item -Recurse -Force '{temp_dir}'" diff --git a/godot/icon.svg b/godot/icon.svg new file mode 100644 index 0000000..a172ba0 --- /dev/null +++ b/godot/icon.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/godot/icon.svg.import b/godot/icon.svg.import new file mode 100644 index 0000000..9bbfd5e --- /dev/null +++ b/godot/icon.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dl5hs6rdpmoec" +path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icon.svg" +dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/godot/icons/closed_path.svg b/godot/icons/closed_path.svg new file mode 100644 index 0000000..f5d1f65 --- /dev/null +++ b/godot/icons/closed_path.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/godot/icons/closed_path.svg.import b/godot/icons/closed_path.svg.import new file mode 100644 index 0000000..b3199ac --- /dev/null +++ b/godot/icons/closed_path.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b5qkp4rnmh7p5" +path="res://.godot/imported/closed_path.svg-cdd564bbac96eac66182d47b2532fae5.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icons/closed_path.svg" +dest_files=["res://.godot/imported/closed_path.svg-cdd564bbac96eac66182d47b2532fae5.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/godot/icons/diff.svg b/godot/icons/diff.svg new file mode 100644 index 0000000..ac856ac --- /dev/null +++ b/godot/icons/diff.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/godot/icons/diff.svg.import b/godot/icons/diff.svg.import new file mode 100644 index 0000000..8cecc4c --- /dev/null +++ b/godot/icons/diff.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://esb812dxjhx3" +path="res://.godot/imported/diff.svg-89f4448e2c6de55a354a8b2fb2217542.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icons/diff.svg" +dest_files=["res://.godot/imported/diff.svg-89f4448e2c6de55a354a8b2fb2217542.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=0.04 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/godot/icons/fill-plus.svg b/godot/icons/fill-plus.svg new file mode 100644 index 0000000..c2f8f0e --- /dev/null +++ b/godot/icons/fill-plus.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/godot/icons/fill-plus.svg.import b/godot/icons/fill-plus.svg.import new file mode 100644 index 0000000..5ba3c3f --- /dev/null +++ b/godot/icons/fill-plus.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://clwpv24en3js7" +path="res://.godot/imported/fill-plus.svg-3288def27577982d1d5155c9192bf46a.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icons/fill-plus.svg" +dest_files=["res://.godot/imported/fill-plus.svg-3288def27577982d1d5155c9192bf46a.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=0.04 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/godot/icons/folder-plus.svg b/godot/icons/folder-plus.svg new file mode 100644 index 0000000..810107c --- /dev/null +++ b/godot/icons/folder-plus.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/godot/icons/folder-plus.svg.import b/godot/icons/folder-plus.svg.import new file mode 100644 index 0000000..bced747 --- /dev/null +++ b/godot/icons/folder-plus.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dsgcgfj17p0ih" +path="res://.godot/imported/folder-plus.svg-b15e9c4a6f857cee59ac6d25bde07ea9.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icons/folder-plus.svg" +dest_files=["res://.godot/imported/folder-plus.svg-b15e9c4a6f857cee59ac6d25bde07ea9.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=0.04 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/godot/icons/font_awesome/angle-right-solid.svg b/godot/icons/font_awesome/angle-right-solid.svg new file mode 100644 index 0000000..6c90c2c --- /dev/null +++ b/godot/icons/font_awesome/angle-right-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/godot/icons/font_awesome/angle-right-solid.svg.import b/godot/icons/font_awesome/angle-right-solid.svg.import new file mode 100644 index 0000000..c3369aa --- /dev/null +++ b/godot/icons/font_awesome/angle-right-solid.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dn5t4s8yc6bm3" +path="res://.godot/imported/angle-right-solid.svg-15f903df9b1078a02a46c0fd3d3f7a90.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icons/font_awesome/angle-right-solid.svg" +dest_files=["res://.godot/imported/angle-right-solid.svg-15f903df9b1078a02a46c0fd3d3f7a90.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/godot/icons/font_awesome/arrow-pointer-solid.svg b/godot/icons/font_awesome/arrow-pointer-solid.svg new file mode 100644 index 0000000..121ffc3 --- /dev/null +++ b/godot/icons/font_awesome/arrow-pointer-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/godot/icons/font_awesome/arrow-pointer-solid.svg.import b/godot/icons/font_awesome/arrow-pointer-solid.svg.import new file mode 100644 index 0000000..05efea3 --- /dev/null +++ b/godot/icons/font_awesome/arrow-pointer-solid.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://m4cppsxsjaem" +path="res://.godot/imported/arrow-pointer-solid.svg-d073a31647104f283990d10a3eaa36f8.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icons/font_awesome/arrow-pointer-solid.svg" +dest_files=["res://.godot/imported/arrow-pointer-solid.svg-d073a31647104f283990d10a3eaa36f8.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/godot/icons/font_awesome/arrow-right-arrow-left-solid.svg b/godot/icons/font_awesome/arrow-right-arrow-left-solid.svg new file mode 100644 index 0000000..2322a99 --- /dev/null +++ b/godot/icons/font_awesome/arrow-right-arrow-left-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/godot/icons/font_awesome/arrow-right-arrow-left-solid.svg.import b/godot/icons/font_awesome/arrow-right-arrow-left-solid.svg.import new file mode 100644 index 0000000..5360635 --- /dev/null +++ b/godot/icons/font_awesome/arrow-right-arrow-left-solid.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cnqkj40h44qeh" +path="res://.godot/imported/arrow-right-arrow-left-solid.svg-bd68e50487711bfc723f271487cbec4a.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icons/font_awesome/arrow-right-arrow-left-solid.svg" +dest_files=["res://.godot/imported/arrow-right-arrow-left-solid.svg-bd68e50487711bfc723f271487cbec4a.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/godot/icons/font_awesome/caret-down-solid.svg b/godot/icons/font_awesome/caret-down-solid.svg new file mode 100644 index 0000000..6347082 --- /dev/null +++ b/godot/icons/font_awesome/caret-down-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/godot/icons/font_awesome/caret-down-solid.svg.import b/godot/icons/font_awesome/caret-down-solid.svg.import new file mode 100644 index 0000000..5adfbf9 --- /dev/null +++ b/godot/icons/font_awesome/caret-down-solid.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bihym07yhfwvb" +path="res://.godot/imported/caret-down-solid.svg-62e766fe9b1eca7b51f726e385c4f5c8.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icons/font_awesome/caret-down-solid.svg" +dest_files=["res://.godot/imported/caret-down-solid.svg-62e766fe9b1eca7b51f726e385c4f5c8.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/godot/icons/font_awesome/caret-right-solid.svg b/godot/icons/font_awesome/caret-right-solid.svg new file mode 100644 index 0000000..fc1e8dc --- /dev/null +++ b/godot/icons/font_awesome/caret-right-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/godot/icons/font_awesome/caret-right-solid.svg.import b/godot/icons/font_awesome/caret-right-solid.svg.import new file mode 100644 index 0000000..b4deb53 --- /dev/null +++ b/godot/icons/font_awesome/caret-right-solid.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://1jifl7a7wmah" +path="res://.godot/imported/caret-right-solid.svg-d38c22ea1063fb55fd46e0c4969af69e.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icons/font_awesome/caret-right-solid.svg" +dest_files=["res://.godot/imported/caret-right-solid.svg-d38c22ea1063fb55fd46e0c4969af69e.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/godot/icons/font_awesome/circle-question-solid.svg b/godot/icons/font_awesome/circle-question-solid.svg new file mode 100644 index 0000000..3cc83fd --- /dev/null +++ b/godot/icons/font_awesome/circle-question-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/godot/icons/font_awesome/circle-question-solid.svg.import b/godot/icons/font_awesome/circle-question-solid.svg.import new file mode 100644 index 0000000..6ce1dfc --- /dev/null +++ b/godot/icons/font_awesome/circle-question-solid.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b4pppiaal50ew" +path="res://.godot/imported/circle-question-solid.svg-2291510bf7318e6cfc3679aba0f33334.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icons/font_awesome/circle-question-solid.svg" +dest_files=["res://.godot/imported/circle-question-solid.svg-2291510bf7318e6cfc3679aba0f33334.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=0.06 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/godot/icons/font_awesome/circle-regular.svg b/godot/icons/font_awesome/circle-regular.svg new file mode 100644 index 0000000..6bd5a9f --- /dev/null +++ b/godot/icons/font_awesome/circle-regular.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/godot/icons/font_awesome/circle-regular.svg.import b/godot/icons/font_awesome/circle-regular.svg.import new file mode 100644 index 0000000..0937773 --- /dev/null +++ b/godot/icons/font_awesome/circle-regular.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cwvu8b6n6ba6b" +path="res://.godot/imported/circle-regular.svg-7041da5835c4bf1e0b8a60ad64a1b00b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icons/font_awesome/circle-regular.svg" +dest_files=["res://.godot/imported/circle-regular.svg-7041da5835c4bf1e0b8a60ad64a1b00b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/godot/icons/font_awesome/circle-solid.svg b/godot/icons/font_awesome/circle-solid.svg new file mode 100644 index 0000000..542206b --- /dev/null +++ b/godot/icons/font_awesome/circle-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/godot/icons/font_awesome/circle-solid.svg.import b/godot/icons/font_awesome/circle-solid.svg.import new file mode 100644 index 0000000..dd5f533 --- /dev/null +++ b/godot/icons/font_awesome/circle-solid.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://d4dxklmx6r8is" +path="res://.godot/imported/circle-solid.svg-c861deddc622935b33913731c609265c.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icons/font_awesome/circle-solid.svg" +dest_files=["res://.godot/imported/circle-solid.svg-c861deddc622935b33913731c609265c.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/godot/icons/font_awesome/clone-regular.svg b/godot/icons/font_awesome/clone-regular.svg new file mode 100644 index 0000000..e85aefc --- /dev/null +++ b/godot/icons/font_awesome/clone-regular.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/godot/icons/font_awesome/clone-regular.svg.import b/godot/icons/font_awesome/clone-regular.svg.import new file mode 100644 index 0000000..4e1b8df --- /dev/null +++ b/godot/icons/font_awesome/clone-regular.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ukf4j5kwqbqc" +path="res://.godot/imported/clone-regular.svg-6f5da595d139f68a557b019b2cc2a636.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icons/font_awesome/clone-regular.svg" +dest_files=["res://.godot/imported/clone-regular.svg-6f5da595d139f68a557b019b2cc2a636.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/godot/icons/font_awesome/expand-solid.svg b/godot/icons/font_awesome/expand-solid.svg new file mode 100644 index 0000000..c2fe42a --- /dev/null +++ b/godot/icons/font_awesome/expand-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/godot/icons/font_awesome/expand-solid.svg.import b/godot/icons/font_awesome/expand-solid.svg.import new file mode 100644 index 0000000..f339a69 --- /dev/null +++ b/godot/icons/font_awesome/expand-solid.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://yopfar782wsm" +path="res://.godot/imported/expand-solid.svg-cfd5ffe47bbe6e5ad8fb7566fbbf1dbb.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icons/font_awesome/expand-solid.svg" +dest_files=["res://.godot/imported/expand-solid.svg-cfd5ffe47bbe6e5ad8fb7566fbbf1dbb.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/godot/icons/font_awesome/eye-slash-solid.svg b/godot/icons/font_awesome/eye-slash-solid.svg new file mode 100644 index 0000000..eedcdb7 --- /dev/null +++ b/godot/icons/font_awesome/eye-slash-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/godot/icons/font_awesome/eye-slash-solid.svg.import b/godot/icons/font_awesome/eye-slash-solid.svg.import new file mode 100644 index 0000000..cb11332 --- /dev/null +++ b/godot/icons/font_awesome/eye-slash-solid.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bs6y5xvpid1hl" +path="res://.godot/imported/eye-slash-solid.svg-578b908dc7a7469df7258eec21dcd6cd.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icons/font_awesome/eye-slash-solid.svg" +dest_files=["res://.godot/imported/eye-slash-solid.svg-578b908dc7a7469df7258eec21dcd6cd.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/godot/icons/font_awesome/eye-solid.svg b/godot/icons/font_awesome/eye-solid.svg new file mode 100644 index 0000000..963efc3 --- /dev/null +++ b/godot/icons/font_awesome/eye-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/godot/icons/font_awesome/eye-solid.svg.import b/godot/icons/font_awesome/eye-solid.svg.import new file mode 100644 index 0000000..8bce123 --- /dev/null +++ b/godot/icons/font_awesome/eye-solid.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cp43s5pjuigfc" +path="res://.godot/imported/eye-solid.svg-0949cc6e0b3133b24acec925654262c6.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icons/font_awesome/eye-solid.svg" +dest_files=["res://.godot/imported/eye-solid.svg-0949cc6e0b3133b24acec925654262c6.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/godot/icons/font_awesome/fill-solid.svg b/godot/icons/font_awesome/fill-solid.svg new file mode 100644 index 0000000..6a78913 --- /dev/null +++ b/godot/icons/font_awesome/fill-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/godot/icons/font_awesome/fill-solid.svg.import b/godot/icons/font_awesome/fill-solid.svg.import new file mode 100644 index 0000000..51d263b --- /dev/null +++ b/godot/icons/font_awesome/fill-solid.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c8eyxuhtxxx2w" +path="res://.godot/imported/fill-solid.svg-6fabddf3a29a24efce44391181ea6cf0.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icons/font_awesome/fill-solid.svg" +dest_files=["res://.godot/imported/fill-solid.svg-6fabddf3a29a24efce44391181ea6cf0.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/godot/icons/font_awesome/folder-open-regular.svg b/godot/icons/font_awesome/folder-open-regular.svg new file mode 100644 index 0000000..3812aa4 --- /dev/null +++ b/godot/icons/font_awesome/folder-open-regular.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/godot/icons/font_awesome/folder-open-regular.svg.import b/godot/icons/font_awesome/folder-open-regular.svg.import new file mode 100644 index 0000000..dcaca5f --- /dev/null +++ b/godot/icons/font_awesome/folder-open-regular.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://yof4lmve8kdo" +path="res://.godot/imported/folder-open-regular.svg-d902fe499e61377e4a3542c7847083fd.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icons/font_awesome/folder-open-regular.svg" +dest_files=["res://.godot/imported/folder-open-regular.svg-d902fe499e61377e4a3542c7847083fd.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=0.04 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/godot/icons/font_awesome/folder-regular.svg b/godot/icons/font_awesome/folder-regular.svg new file mode 100644 index 0000000..ded8388 --- /dev/null +++ b/godot/icons/font_awesome/folder-regular.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/godot/icons/font_awesome/folder-regular.svg.import b/godot/icons/font_awesome/folder-regular.svg.import new file mode 100644 index 0000000..49c13fe --- /dev/null +++ b/godot/icons/font_awesome/folder-regular.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dbdfmeeypughg" +path="res://.godot/imported/folder-regular.svg-edfbe50a09d44da5a22c6f62b2daa2e3.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icons/font_awesome/folder-regular.svg" +dest_files=["res://.godot/imported/folder-regular.svg-edfbe50a09d44da5a22c6f62b2daa2e3.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=0.04 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/godot/icons/font_awesome/left-right-solid.svg b/godot/icons/font_awesome/left-right-solid.svg new file mode 100644 index 0000000..d7fad87 --- /dev/null +++ b/godot/icons/font_awesome/left-right-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/godot/icons/font_awesome/left-right-solid.svg.import b/godot/icons/font_awesome/left-right-solid.svg.import new file mode 100644 index 0000000..b8b0a38 --- /dev/null +++ b/godot/icons/font_awesome/left-right-solid.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cfxa3i3lacmvf" +path="res://.godot/imported/left-right-solid.svg-e85fdfd6cce6fbf75ee37d731383a00b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icons/font_awesome/left-right-solid.svg" +dest_files=["res://.godot/imported/left-right-solid.svg-e85fdfd6cce6fbf75ee37d731383a00b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/godot/icons/font_awesome/location-arrow-solid.svg b/godot/icons/font_awesome/location-arrow-solid.svg new file mode 100644 index 0000000..120ed14 --- /dev/null +++ b/godot/icons/font_awesome/location-arrow-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/godot/icons/font_awesome/location-arrow-solid.svg.import b/godot/icons/font_awesome/location-arrow-solid.svg.import new file mode 100644 index 0000000..70c069f --- /dev/null +++ b/godot/icons/font_awesome/location-arrow-solid.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cdcr07emv7u2m" +path="res://.godot/imported/location-arrow-solid.svg-49da542b3a68ff7aaae3c453a5827a9b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icons/font_awesome/location-arrow-solid.svg" +dest_files=["res://.godot/imported/location-arrow-solid.svg-49da542b3a68ff7aaae3c453a5827a9b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/godot/icons/font_awesome/lock-open-solid.svg b/godot/icons/font_awesome/lock-open-solid.svg new file mode 100644 index 0000000..ed55ef4 --- /dev/null +++ b/godot/icons/font_awesome/lock-open-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/godot/icons/font_awesome/lock-open-solid.svg.import b/godot/icons/font_awesome/lock-open-solid.svg.import new file mode 100644 index 0000000..6936aa7 --- /dev/null +++ b/godot/icons/font_awesome/lock-open-solid.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dc2f1ibod6vkx" +path="res://.godot/imported/lock-open-solid.svg-5bd01cc842d324d470b62d38a6fdf978.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icons/font_awesome/lock-open-solid.svg" +dest_files=["res://.godot/imported/lock-open-solid.svg-5bd01cc842d324d470b62d38a6fdf978.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/godot/icons/font_awesome/lock-solid.svg b/godot/icons/font_awesome/lock-solid.svg new file mode 100644 index 0000000..6aeaa85 --- /dev/null +++ b/godot/icons/font_awesome/lock-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/godot/icons/font_awesome/lock-solid.svg.import b/godot/icons/font_awesome/lock-solid.svg.import new file mode 100644 index 0000000..a835330 --- /dev/null +++ b/godot/icons/font_awesome/lock-solid.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bgaugxuhl5lvm" +path="res://.godot/imported/lock-solid.svg-e1766748a3d513c5c2c2cf185aa5071e.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icons/font_awesome/lock-solid.svg" +dest_files=["res://.godot/imported/lock-solid.svg-e1766748a3d513c5c2c2cf185aa5071e.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/godot/icons/font_awesome/magnifying-glass-solid.svg b/godot/icons/font_awesome/magnifying-glass-solid.svg new file mode 100644 index 0000000..486585f --- /dev/null +++ b/godot/icons/font_awesome/magnifying-glass-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/godot/icons/font_awesome/magnifying-glass-solid.svg.import b/godot/icons/font_awesome/magnifying-glass-solid.svg.import new file mode 100644 index 0000000..aa24cf4 --- /dev/null +++ b/godot/icons/font_awesome/magnifying-glass-solid.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bej0ujfdimntj" +path="res://.godot/imported/magnifying-glass-solid.svg-8cf6428eeedd7c2688a8bd6f78374e02.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icons/font_awesome/magnifying-glass-solid.svg" +dest_files=["res://.godot/imported/magnifying-glass-solid.svg-8cf6428eeedd7c2688a8bd6f78374e02.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/godot/icons/font_awesome/maximize-solid.svg b/godot/icons/font_awesome/maximize-solid.svg new file mode 100644 index 0000000..3b1500c --- /dev/null +++ b/godot/icons/font_awesome/maximize-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/godot/icons/font_awesome/maximize-solid.svg.import b/godot/icons/font_awesome/maximize-solid.svg.import new file mode 100644 index 0000000..7fd80f4 --- /dev/null +++ b/godot/icons/font_awesome/maximize-solid.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://q7ksryj1gucs" +path="res://.godot/imported/maximize-solid.svg-094bfde1f66c027814cfa18a3879b868.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icons/font_awesome/maximize-solid.svg" +dest_files=["res://.godot/imported/maximize-solid.svg-094bfde1f66c027814cfa18a3879b868.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/godot/icons/font_awesome/pen-nib-solid.svg b/godot/icons/font_awesome/pen-nib-solid.svg new file mode 100644 index 0000000..daa86b1 --- /dev/null +++ b/godot/icons/font_awesome/pen-nib-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/godot/icons/font_awesome/pen-nib-solid.svg.import b/godot/icons/font_awesome/pen-nib-solid.svg.import new file mode 100644 index 0000000..78dad68 --- /dev/null +++ b/godot/icons/font_awesome/pen-nib-solid.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://07j3j13jje5v" +path="res://.godot/imported/pen-nib-solid.svg-3cf0ec97f9f499c26873e5fbb9a3fc00.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icons/font_awesome/pen-nib-solid.svg" +dest_files=["res://.godot/imported/pen-nib-solid.svg-3cf0ec97f9f499c26873e5fbb9a3fc00.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/godot/icons/font_awesome/plus-solid.svg b/godot/icons/font_awesome/plus-solid.svg new file mode 100644 index 0000000..b83f121 --- /dev/null +++ b/godot/icons/font_awesome/plus-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/godot/icons/font_awesome/plus-solid.svg.import b/godot/icons/font_awesome/plus-solid.svg.import new file mode 100644 index 0000000..c7d40d4 --- /dev/null +++ b/godot/icons/font_awesome/plus-solid.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://6u0brruaecvq" +path="res://.godot/imported/plus-solid.svg-889d572bfffa878592bb95dc78e95e75.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icons/font_awesome/plus-solid.svg" +dest_files=["res://.godot/imported/plus-solid.svg-889d572bfffa878592bb95dc78e95e75.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/godot/icons/font_awesome/rotate-left-solid.svg b/godot/icons/font_awesome/rotate-left-solid.svg new file mode 100644 index 0000000..36c5306 --- /dev/null +++ b/godot/icons/font_awesome/rotate-left-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/godot/icons/font_awesome/rotate-left-solid.svg.import b/godot/icons/font_awesome/rotate-left-solid.svg.import new file mode 100644 index 0000000..2a40520 --- /dev/null +++ b/godot/icons/font_awesome/rotate-left-solid.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://x10k6c7p5yn2" +path="res://.godot/imported/rotate-left-solid.svg-a893aca4dd997050aa3c72402e666513.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icons/font_awesome/rotate-left-solid.svg" +dest_files=["res://.godot/imported/rotate-left-solid.svg-a893aca4dd997050aa3c72402e666513.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/godot/icons/font_awesome/slash-solid.svg b/godot/icons/font_awesome/slash-solid.svg new file mode 100644 index 0000000..51df816 --- /dev/null +++ b/godot/icons/font_awesome/slash-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/godot/icons/font_awesome/slash-solid.svg.import b/godot/icons/font_awesome/slash-solid.svg.import new file mode 100644 index 0000000..ed6e422 --- /dev/null +++ b/godot/icons/font_awesome/slash-solid.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://byheik8rybqlc" +path="res://.godot/imported/slash-solid.svg-88c359b4b493493b74f96a76634819f2.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icons/font_awesome/slash-solid.svg" +dest_files=["res://.godot/imported/slash-solid.svg-88c359b4b493493b74f96a76634819f2.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/godot/icons/font_awesome/square-regular.svg b/godot/icons/font_awesome/square-regular.svg new file mode 100644 index 0000000..ff1c29c --- /dev/null +++ b/godot/icons/font_awesome/square-regular.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/godot/icons/font_awesome/square-regular.svg.import b/godot/icons/font_awesome/square-regular.svg.import new file mode 100644 index 0000000..f5d0d90 --- /dev/null +++ b/godot/icons/font_awesome/square-regular.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://w3jj8dsk5ee" +path="res://.godot/imported/square-regular.svg-9ec8f713fc388e11940ce8e6ba39195b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icons/font_awesome/square-regular.svg" +dest_files=["res://.godot/imported/square-regular.svg-9ec8f713fc388e11940ce8e6ba39195b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/godot/icons/font_awesome/square-solid.svg b/godot/icons/font_awesome/square-solid.svg new file mode 100644 index 0000000..5110ddd --- /dev/null +++ b/godot/icons/font_awesome/square-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/godot/icons/font_awesome/square-solid.svg.import b/godot/icons/font_awesome/square-solid.svg.import new file mode 100644 index 0000000..29cff8c --- /dev/null +++ b/godot/icons/font_awesome/square-solid.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dfnl502akgfg8" +path="res://.godot/imported/square-solid.svg-2daf954ce57320ca79dd33f1f261a7f6.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icons/font_awesome/square-solid.svg" +dest_files=["res://.godot/imported/square-solid.svg-2daf954ce57320ca79dd33f1f261a7f6.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/godot/icons/font_awesome/trash-solid.svg b/godot/icons/font_awesome/trash-solid.svg new file mode 100644 index 0000000..69cc3fa --- /dev/null +++ b/godot/icons/font_awesome/trash-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/godot/icons/font_awesome/trash-solid.svg.import b/godot/icons/font_awesome/trash-solid.svg.import new file mode 100644 index 0000000..040ebe5 --- /dev/null +++ b/godot/icons/font_awesome/trash-solid.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cfy4c56trjpcp" +path="res://.godot/imported/trash-solid.svg-5ff44a104faaab9e0722ddd7e4b84d30.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icons/font_awesome/trash-solid.svg" +dest_files=["res://.godot/imported/trash-solid.svg-5ff44a104faaab9e0722ddd7e4b84d30.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/godot/icons/font_awesome/up-down-left-right-solid.svg b/godot/icons/font_awesome/up-down-left-right-solid.svg new file mode 100644 index 0000000..3e59146 --- /dev/null +++ b/godot/icons/font_awesome/up-down-left-right-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/godot/icons/font_awesome/up-down-left-right-solid.svg.import b/godot/icons/font_awesome/up-down-left-right-solid.svg.import new file mode 100644 index 0000000..599d363 --- /dev/null +++ b/godot/icons/font_awesome/up-down-left-right-solid.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b2g3521wwvxi5" +path="res://.godot/imported/up-down-left-right-solid.svg-a97f7a62dd701b192298c5ac7af9ad04.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icons/font_awesome/up-down-left-right-solid.svg" +dest_files=["res://.godot/imported/up-down-left-right-solid.svg-a97f7a62dd701b192298c5ac7af9ad04.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/godot/icons/font_awesome/up-down-solid.svg b/godot/icons/font_awesome/up-down-solid.svg new file mode 100644 index 0000000..a3930b9 --- /dev/null +++ b/godot/icons/font_awesome/up-down-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/godot/icons/font_awesome/up-down-solid.svg.import b/godot/icons/font_awesome/up-down-solid.svg.import new file mode 100644 index 0000000..50ef1ab --- /dev/null +++ b/godot/icons/font_awesome/up-down-solid.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b7h1663aq7quo" +path="res://.godot/imported/up-down-solid.svg-864ef8087e0899dbff5bccdf96a31127.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icons/font_awesome/up-down-solid.svg" +dest_files=["res://.godot/imported/up-down-solid.svg-864ef8087e0899dbff5bccdf96a31127.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/godot/icons/font_awesome/wrench-solid.svg b/godot/icons/font_awesome/wrench-solid.svg new file mode 100644 index 0000000..ca9975a --- /dev/null +++ b/godot/icons/font_awesome/wrench-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/godot/icons/font_awesome/wrench-solid.svg.import b/godot/icons/font_awesome/wrench-solid.svg.import new file mode 100644 index 0000000..2943a9f --- /dev/null +++ b/godot/icons/font_awesome/wrench-solid.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://oqlqlwg6cqcm" +path="res://.godot/imported/wrench-solid.svg-7e09ffd980998fbe06e7903373405527.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icons/font_awesome/wrench-solid.svg" +dest_files=["res://.godot/imported/wrench-solid.svg-7e09ffd980998fbe06e7903373405527.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/godot/icons/gradient-editor-color-arrow.svg b/godot/icons/gradient-editor-color-arrow.svg new file mode 100644 index 0000000..6c76d58 --- /dev/null +++ b/godot/icons/gradient-editor-color-arrow.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/godot/icons/gradient-editor-color-arrow.svg.import b/godot/icons/gradient-editor-color-arrow.svg.import new file mode 100644 index 0000000..d494436 --- /dev/null +++ b/godot/icons/gradient-editor-color-arrow.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cwtuhfpnanbh8" +path="res://.godot/imported/gradient-editor-color-arrow.svg-8bacc587e83374f347c0c6ceb94a6658.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icons/gradient-editor-color-arrow.svg" +dest_files=["res://.godot/imported/gradient-editor-color-arrow.svg-8bacc587e83374f347c0c6ceb94a6658.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/godot/icons/intersect.svg b/godot/icons/intersect.svg new file mode 100644 index 0000000..16b5d45 --- /dev/null +++ b/godot/icons/intersect.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/godot/icons/intersect.svg.import b/godot/icons/intersect.svg.import new file mode 100644 index 0000000..5df99dc --- /dev/null +++ b/godot/icons/intersect.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://delwiq2ud4o5b" +path="res://.godot/imported/intersect.svg-2d5fa6b43577041ecf0f2a4725e55ce1.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icons/intersect.svg" +dest_files=["res://.godot/imported/intersect.svg-2d5fa6b43577041ecf0f2a4725e55ce1.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=0.04 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/godot/icons/mirror-flip.svg b/godot/icons/mirror-flip.svg new file mode 100644 index 0000000..9a283b1 --- /dev/null +++ b/godot/icons/mirror-flip.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/godot/icons/mirror-flip.svg.import b/godot/icons/mirror-flip.svg.import new file mode 100644 index 0000000..45b61af --- /dev/null +++ b/godot/icons/mirror-flip.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bsxn6okjahtn5" +path="res://.godot/imported/mirror-flip.svg-a7c045dfbf76914b4fa2e83bd1a88221.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icons/mirror-flip.svg" +dest_files=["res://.godot/imported/mirror-flip.svg-a7c045dfbf76914b4fa2e83bd1a88221.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/godot/icons/mirror.svg b/godot/icons/mirror.svg new file mode 100644 index 0000000..7162338 --- /dev/null +++ b/godot/icons/mirror.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/godot/icons/mirror.svg.import b/godot/icons/mirror.svg.import new file mode 100644 index 0000000..92d6d3d --- /dev/null +++ b/godot/icons/mirror.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dyvcxrvw3yagv" +path="res://.godot/imported/mirror.svg-b27b93c8da8d7d719c8a00082f3d1136.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icons/mirror.svg" +dest_files=["res://.godot/imported/mirror.svg-b27b93c8da8d7d719c8a00082f3d1136.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/godot/icons/open_path.svg b/godot/icons/open_path.svg new file mode 100644 index 0000000..71d0001 --- /dev/null +++ b/godot/icons/open_path.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/godot/icons/open_path.svg.import b/godot/icons/open_path.svg.import new file mode 100644 index 0000000..093a14c --- /dev/null +++ b/godot/icons/open_path.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://mrvjp3pgu7ea" +path="res://.godot/imported/open_path.svg-ac1b2a4a157ca0fc6d58aab1ec20d81a.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icons/open_path.svg" +dest_files=["res://.godot/imported/open_path.svg-ac1b2a4a157ca0fc6d58aab1ec20d81a.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/godot/icons/square-plus.svg b/godot/icons/square-plus.svg new file mode 100644 index 0000000..09e5020 --- /dev/null +++ b/godot/icons/square-plus.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/godot/icons/square-plus.svg.import b/godot/icons/square-plus.svg.import new file mode 100644 index 0000000..aa020f4 --- /dev/null +++ b/godot/icons/square-plus.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://eej4fsmgxa1d" +path="res://.godot/imported/square-plus.svg-d3eaf9c1f403f080558c039da4a3cbed.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icons/square-plus.svg" +dest_files=["res://.godot/imported/square-plus.svg-d3eaf9c1f403f080558c039da4a3cbed.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=0.04 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/godot/icons/union.svg b/godot/icons/union.svg new file mode 100644 index 0000000..5b0d9e6 --- /dev/null +++ b/godot/icons/union.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/godot/icons/union.svg.import b/godot/icons/union.svg.import new file mode 100644 index 0000000..013fa88 --- /dev/null +++ b/godot/icons/union.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bkls8jpsc3ps5" +path="res://.godot/imported/union.svg-9be719cedb7dbb8ac653c25813c6420c.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icons/union.svg" +dest_files=["res://.godot/imported/union.svg-9be719cedb7dbb8ac653c25813c6420c.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=0.04 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/godot/icons/xor.svg b/godot/icons/xor.svg new file mode 100644 index 0000000..4f6a881 --- /dev/null +++ b/godot/icons/xor.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/godot/icons/xor.svg.import b/godot/icons/xor.svg.import new file mode 100644 index 0000000..e2ff7b8 --- /dev/null +++ b/godot/icons/xor.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bsxiknl4vq61y" +path="res://.godot/imported/xor.svg-1f9b85d073bdcd12ca0ee263f682123e.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icons/xor.svg" +dest_files=["res://.godot/imported/xor.svg-1f9b85d073bdcd12ca0ee263f682123e.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=0.04 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/godot/licenses.txt b/godot/licenses.txt new file mode 100644 index 0000000..8d38cdb --- /dev/null +++ b/godot/licenses.txt @@ -0,0 +1,4036 @@ +[center][b]Godot[/b][/center] + +Copyright (c) 2014-present Godot Engine contributors (see [url="https://github.com/godotengine/godot/blob/master/AUTHORS.md"]AUTHORS.md[/url]). +Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]godot-dockable-container[/b][/center] + +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. + +[center]=== === ===[/center] + + +[center][b]godot-uuid[/b][/center] + +MIT License + +Copyright (c) 2023 Xavier Sellier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]unicode-ident[/b][/center] + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]unicode-ident[/b][/center] + +UNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWARE + +See Terms of Use +for definitions of Unicode Inc.’s Data Files and Software. + +NOTICE TO USER: Carefully read the following legal agreement. +BY DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING UNICODE INC.'S +DATA FILES ("DATA FILES"), AND/OR SOFTWARE ("SOFTWARE"), +YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE +TERMS AND CONDITIONS OF THIS AGREEMENT. +IF YOU DO NOT AGREE, DO NOT DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE +THE DATA FILES OR SOFTWARE. + +COPYRIGHT AND PERMISSION NOTICE + +Copyright © 1991-2022 Unicode, Inc. All rights reserved. +Distributed under the Terms of Use in https://www.unicode.org/copyright.html. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Unicode data files and any associated documentation +(the "Data Files") or Unicode software and any associated documentation +(the "Software") to deal in the Data Files or Software +without restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, and/or sell copies of +the Data Files or Software, and to permit persons to whom the Data Files +or Software are furnished to do so, provided that either +(a) this copyright and permission notice appear with all copies +of the Data Files or Software, or +(b) this copyright and permission notice appear in associated +Documentation. + +THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT OF THIRD PARTY RIGHTS. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS +NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL +DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, +DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THE DATA FILES OR SOFTWARE. + +Except as contained in this notice, the name of a copyright holder +shall not be used in advertising or otherwise to promote the sale, +use or other dealings in these Data Files or Software without prior +written authorization of the copyright holder. + +[center]=== === ===[/center] + + + +[center][b]wasi[/b][/center] + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]cfg-if[/b][/center] + +Copyright (c) 2014 Alex Crichton + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]gensym[/b][/center] + +Copyright (c) 2015 The cargo-readme Developers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]getrandom[/b][/center] + +Copyright (c) 2018-2024 The rust-random Project Developers +Copyright (c) 2014 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]glam[/b][/center] + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]heck[/b][/center] + +Copyright (c) 2015 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]libc[/b][/center] + +Copyright (c) 2014-2020 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]nanoserde[/b][/center] + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +[center]=== === ===[/center] + + +[center][b]paste[/b][/center] + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]proc-macro2[/b][/center] + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]quote[/b][/center] + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]regex[/b][/center] + +Copyright (c) 2014 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]regex-automata[/b][/center] + +Copyright (c) 2014 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]regex-syntax[/b][/center] + +Copyright (c) 2014 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]serde[/b][/center] + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]serde_derive[/b][/center] + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]syn[/b][/center] + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]uuid[/b][/center] + +Copyright (c) 2014 The Rust Project Developers +Copyright (c) 2018 Ashley Mannix, Christopher Armstrong, Dylan DPC, Hunar Roop Kahlon + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]i_float[/b][/center] + +MIT License + +Copyright (c) 2023 iShape-Rust + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]i_overlay[/b][/center] + +MIT License + +Copyright (c) 2023 iShape-Rust + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]i_shape[/b][/center] + +MIT License + +Copyright (c) 2023 iShape-Rust + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]i_tree[/b][/center] + +MIT License + +Copyright (c) 2024 iShape-Rust + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]i_triangle[/b][/center] + +MIT License + +Copyright (c) 2023 iShape-Rust + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]nanoserde-derive[/b][/center] + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]venial[/b][/center] + +MIT License + +Copyright (c) 2022 Olivier FAURE + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]aho-corasick[/b][/center] + +The MIT License (MIT) + +Copyright (c) 2015 Andrew Gallant + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]memchr[/b][/center] + +The MIT License (MIT) + +Copyright (c) 2015 Andrew Gallant + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]gdextension-api[/b][/center] + +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at https://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. + +[center]=== === ===[/center] + + +[center][b]godot[/b][/center] + +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at https://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. + +[center]=== === ===[/center] + + +[center][b]godot-bindings[/b][/center] + +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at https://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. + +[center]=== === ===[/center] + + +[center][b]godot-cell[/b][/center] + +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at https://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. + +[center]=== === ===[/center] + + +[center][b]godot-codegen[/b][/center] + +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at https://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. + +[center]=== === ===[/center] + + +[center][b]godot-core[/b][/center] + +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at https://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. + +[center]=== === ===[/center] + + +[center][b]godot-ffi[/b][/center] + +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at https://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. + +[center]=== === ===[/center] + + +[center][b]godot-macros[/b][/center] + +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at https://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. diff --git a/godot/project.godot b/godot/project.godot new file mode 100644 index 0000000..0ff2af6 --- /dev/null +++ b/godot/project.godot @@ -0,0 +1,124 @@ +; Engine configuration file. +; It's best edited using the editor UI and not directly, +; since the parameters that go here are not all obvious. +; +; Format: +; [section] ; section goes between [] +; param=value ; assign values to parameters + +config_version=5 + +[application] + +config/name="AquamarinePainter" +run/main_scene="res://scenes/app.tscn" +config/features=PackedStringArray("4.3", "Forward Plus") +boot_splash/image="res://splash-logo.png" +boot_splash/fullsize=false +config/icon="res://icon.svg" +boot_splash/minimum_display_time=250 + +[autoload] + +Main="*res://src/main/main.gd" + +[debug] + +gdscript/warnings/untyped_declaration=1 +gdscript/warnings/unsafe_property_access=1 +gdscript/warnings/unsafe_method_access=1 +gdscript/warnings/unsafe_cast=1 +gdscript/warnings/unsafe_call_argument=1 +gdscript/warnings/integer_division=0 + +[display] + +window/size/viewport_width=1600 +window/size/viewport_height=900 +window/size/initial_position_type=3 +window/subwindows/embed_subwindows=false + +[editor_plugins] + +enabled=PackedStringArray("res://addons/dockable_container/plugin.cfg") + +[filesystem] + +import/blender/enabled=false + +[gui] + +common/snap_controls_to_pixels=false + +[input] + +ui_undo={ +"deadzone": 0.5, +"events": [] +} +ui_redo={ +"deadzone": 0.5, +"events": [] +} +drawing_tool={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":49,"key_label":0,"unicode":49,"location":0,"echo":false,"script":null) +] +} +manipulate_tool={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":50,"key_label":0,"unicode":50,"location":0,"echo":false,"script":null) +] +} +parameter_tuning_tool={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":51,"key_label":0,"unicode":51,"location":0,"echo":false,"script":null) +] +} +expand={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":52,"key_label":0,"unicode":52,"location":0,"echo":false,"script":null) +] +} +zoom_reset={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":53,"key_label":0,"unicode":53,"location":0,"echo":false,"script":null) +] +} +mirror={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":54,"key_label":0,"unicode":54,"location":0,"echo":false,"script":null) +] +} +save={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":true,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} +save_as={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":true,"ctrl_pressed":true,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} +undo={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":true,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":90,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} +redo={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":true,"ctrl_pressed":true,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":90,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":true,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":89,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} + +[internationalization] + +locale/translations=PackedStringArray("res://translation/translation.ja.translation", "res://translation/translation.en.translation") + +[rendering] + +environment/screen_space_reflection/roughness_quality=0 +environment/subsurface_scattering/subsurface_scattering_quality=0 +environment/defaults/default_clear_color=Color(0.301961, 0.301961, 0.301961, 1) +anti_aliasing/quality/msaa_2d=3 diff --git a/godot/scenes/app.tscn b/godot/scenes/app.tscn new file mode 100644 index 0000000..e5674b2 --- /dev/null +++ b/godot/scenes/app.tscn @@ -0,0 +1,207 @@ +[gd_scene load_steps=23 format=3 uid="uid://c1nm8iya05plr"] + +[ext_resource type="Script" path="res://addons/dockable_container/dockable_container.gd" id="1_1np4h"] +[ext_resource type="Script" path="res://src/app.gd" id="1_uojh7"] +[ext_resource type="Script" path="res://addons/dockable_container/layout_panel.gd" id="2_ygcnm"] +[ext_resource type="Script" path="res://addons/dockable_container/layout.gd" id="3_cit1o"] +[ext_resource type="Script" path="res://addons/dockable_container/layout_split.gd" id="3_wke7q"] +[ext_resource type="PackedScene" uid="uid://byc66jalp35s" path="res://scenes/tab/canvas_tab.tscn" id="4_4n2d2"] +[ext_resource type="PackedScene" uid="uid://dcihb270dgr70" path="res://scenes/tab/layer_list_tab.tscn" id="5_l683t"] +[ext_resource type="PackedScene" uid="uid://c8ge45ar23ppv" path="res://scenes/tab/layer_setting_tab.tscn" id="7_i41rk"] +[ext_resource type="PackedScene" uid="uid://blv25euce0b64" path="res://scenes/tab/material_list_tab.tscn" id="8_w7upe"] +[ext_resource type="PackedScene" uid="uid://b0t6n35a5uvxu" path="res://scenes/sub_window/document_open_window.tscn" id="10_uwbbb"] +[ext_resource type="PackedScene" uid="uid://dferwtnot6vxw" path="res://scenes/sub_window/document_export_window.tscn" id="11_1ernd"] +[ext_resource type="PackedScene" uid="uid://7rdbvmswbjw3" path="res://scenes/sub_window/document_size_change_window.tscn" id="12_xatou"] +[ext_resource type="PackedScene" uid="uid://dd4oq7jxlensx" path="res://scenes/sub_window/config_window.tscn" id="13_wpqkv"] +[ext_resource type="PackedScene" uid="uid://b85jraf3yfbfl" path="res://scenes/sub_window/licenses_window.tscn" id="14_b85it"] + +[sub_resource type="Resource" id="Resource_25ecf"] +resource_name = "Tabs" +script = ExtResource("2_ygcnm") +names = PackedStringArray("CanvasTab") +current_tab = 0 + +[sub_resource type="Resource" id="Resource_cyi83"] +resource_name = "Tabs" +script = ExtResource("2_ygcnm") +names = PackedStringArray("Layer") +current_tab = 0 + +[sub_resource type="Resource" id="Resource_h8wjn"] +resource_name = "Split" +script = ExtResource("3_wke7q") +direction = 0 +percent = 0.813725 +first = SubResource("Resource_25ecf") +second = SubResource("Resource_cyi83") + +[sub_resource type="Resource" id="Resource_7fjdv"] +resource_name = "Tabs" +script = ExtResource("2_ygcnm") +names = PackedStringArray("LayerSetting") +current_tab = 0 + +[sub_resource type="Resource" id="Resource_2gxso"] +resource_name = "Tabs" +script = ExtResource("2_ygcnm") +names = PackedStringArray("MaterialList") +current_tab = 0 + +[sub_resource type="Resource" id="Resource_bn7bj"] +resource_name = "Split" +script = ExtResource("3_wke7q") +direction = 1 +percent = 0.1875 +first = SubResource("Resource_7fjdv") +second = SubResource("Resource_2gxso") + +[sub_resource type="Resource" id="Resource_s46d0"] +resource_name = "Split" +script = ExtResource("3_wke7q") +direction = 0 +percent = 0.812339 +first = SubResource("Resource_h8wjn") +second = SubResource("Resource_bn7bj") + +[sub_resource type="Resource" id="Resource_htknp"] +resource_name = "Layout" +script = ExtResource("3_cit1o") +root = SubResource("Resource_s46d0") +hidden_tabs = {} + +[node name="App" type="PanelContainer"] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 +script = ExtResource("1_uojh7") + +[node name="LayerSubViewports" type="Node2D" parent="."] + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +layout_mode = 2 +mouse_filter = 2 + +[node name="Panel" type="Panel" parent="VBoxContainer"] +custom_minimum_size = Vector2(0, 32) +layout_mode = 2 + +[node name="MenuBar" type="MenuBar" parent="VBoxContainer/Panel"] +custom_minimum_size = Vector2(0, 32) +layout_mode = 2 +offset_right = 1152.0 +offset_bottom = 32.0 + +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/Panel/MenuBar"] +layout_mode = 0 +offset_right = 40.0 +offset_bottom = 40.0 +theme_override_constants/separation = 16 + +[node name="FileMenuButton" type="MenuButton" parent="VBoxContainer/Panel/MenuBar/HBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +text = "MENU_FILE" +item_count = 7 +popup/item_0/text = "FILE_NEW_DOCUMENT" +popup/item_1/text = "FILE_OPEN_DOCUMENT" +popup/item_1/id = 1 +popup/item_2/text = "FILE_SAVE" +popup/item_2/id = 2 +popup/item_3/text = "FILE_SAVE_AS" +popup/item_3/id = 3 +popup/item_4/text = "FILE_CLOSE_DOCUMENT" +popup/item_4/id = 4 +popup/item_5/id = 5 +popup/item_5/disabled = true +popup/item_5/separator = true +popup/item_6/text = "FILE_EXPORT_IMAGE" +popup/item_6/id = 6 + +[node name="EditMenuButton" type="MenuButton" parent="VBoxContainer/Panel/MenuBar/HBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +text = "MENU_EDIT" +item_count = 4 +popup/item_0/text = "EDIT_UNDO" +popup/item_1/text = "EDIT_REDO" +popup/item_1/id = 1 +popup/item_2/id = 2 +popup/item_2/disabled = true +popup/item_2/separator = true +popup/item_3/text = "EDIT_CHANGE_DOCUMENT_SIZE" +popup/item_3/id = 3 + +[node name="ToolMenuButton" type="MenuButton" parent="VBoxContainer/Panel/MenuBar/HBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +text = "MENU_TOOL" +item_count = 2 +popup/item_0/text = "TOOL_CONFIG" +popup/item_1/text = "TOOL_LICENSES" +popup/item_1/id = 1 + +[node name="DockableContainer" type="Container" parent="VBoxContainer"] +layout_mode = 2 +size_flags_vertical = 3 +mouse_filter = 2 +script = ExtResource("1_1np4h") +tab_alignment = 0 +hide_single_tab = true +layout = SubResource("Resource_htknp") + +[node name="CanvasTab" parent="VBoxContainer/DockableContainer" instance=ExtResource("4_4n2d2")] +unique_name_in_owner = true +layout_mode = 2 + +[node name="Layer" parent="VBoxContainer/DockableContainer" instance=ExtResource("5_l683t")] +unique_name_in_owner = true +layout_mode = 2 + +[node name="LayerSetting" parent="VBoxContainer/DockableContainer" instance=ExtResource("7_i41rk")] +unique_name_in_owner = true +layout_mode = 2 + +[node name="MaterialList" parent="VBoxContainer/DockableContainer" instance=ExtResource("8_w7upe")] +unique_name_in_owner = true +layout_mode = 2 + +[node name="DocumentOpenWindow" parent="." instance=ExtResource("10_uwbbb")] +unique_name_in_owner = true +visible = false + +[node name="DocumentExportWindow" parent="." instance=ExtResource("11_1ernd")] +unique_name_in_owner = true +visible = false + +[node name="DocumentSizeChangeWindow" parent="." instance=ExtResource("12_xatou")] +unique_name_in_owner = true +visible = false + +[node name="ConfigWindow" parent="." instance=ExtResource("13_wpqkv")] +unique_name_in_owner = true +visible = false + +[node name="LicensesWindow" parent="." instance=ExtResource("14_b85it")] +unique_name_in_owner = true +visible = false + +[node name="FileDialog" type="FileDialog" parent="."] +unique_name_in_owner = true +size = Vector2i(303, 180) +access = 2 +use_native_dialog = true + +[node name="CloseConfirmationDialog" type="ConfirmationDialog" parent="."] +unique_name_in_owner = true +initial_position = 2 +size = Vector2i(233, 106) +ok_button_text = "破棄" +dialog_text = "ドキュメントが保存されていません。 +破棄しますか?" + +[connection signal="on_create_document" from="DocumentOpenWindow" to="." method="_on_document_open_window_on_create_document"] +[connection signal="on_export_document" from="DocumentExportWindow" to="." method="_on_document_export_window_on_export_document"] +[connection signal="on_change_document_size" from="DocumentSizeChangeWindow" to="." method="_on_document_size_change_window_on_change_document_size"] diff --git a/godot/scenes/control/control_linear_gradient.tscn b/godot/scenes/control/control_linear_gradient.tscn new file mode 100644 index 0000000..2dfba36 --- /dev/null +++ b/godot/scenes/control/control_linear_gradient.tscn @@ -0,0 +1,71 @@ +[gd_scene load_steps=8 format=3 uid="uid://be2jf3flgfqum"] + +[ext_resource type="Shader" path="res://shaders/fill.gdshader" id="1_hlr76"] +[ext_resource type="Script" path="res://src/control/control_linear_gradient.gd" id="1_tmiqw"] +[ext_resource type="Texture2D" uid="uid://d4dxklmx6r8is" path="res://icons/font_awesome/circle-solid.svg" id="2_qxkbt"] +[ext_resource type="Texture2D" uid="uid://cwvu8b6n6ba6b" path="res://icons/font_awesome/circle-regular.svg" id="3_itlt2"] + +[sub_resource type="CircleShape2D" id="CircleShape2D_vx16l"] + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_r8oe2"] +shader = ExtResource("1_hlr76") +shader_parameter/fill_color = Color(1, 1, 1, 1) + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_i3wlg"] +shader = ExtResource("1_hlr76") +shader_parameter/fill_color = Color(0, 0, 0, 1) + +[node name="ControlLinearGradient" type="Node2D"] +script = ExtResource("1_tmiqw") + +[node name="Line2D" type="Line2D" parent="."] +unique_name_in_owner = true +points = PackedVector2Array(0, 0, 100, 0) +width = 5.0 +default_color = Color(1, 1, 1, 0.501961) + +[node name="StartPoint" type="Node2D" parent="."] +unique_name_in_owner = true + +[node name="StartArea2D" type="Area2D" parent="StartPoint"] + +[node name="CollisionShape2D" type="CollisionShape2D" parent="StartPoint/StartArea2D"] +shape = SubResource("CircleShape2D_vx16l") + +[node name="StartSprite2D" type="Sprite2D" parent="StartPoint"] +unique_name_in_owner = true +material = SubResource("ShaderMaterial_r8oe2") +scale = Vector2(0.025, 0.025) +texture = ExtResource("2_qxkbt") + +[node name="StartSprite2D2" type="Sprite2D" parent="StartPoint"] +unique_name_in_owner = true +material = SubResource("ShaderMaterial_i3wlg") +scale = Vector2(0.025, 0.025) +texture = ExtResource("3_itlt2") + +[node name="EndPoint" type="Node2D" parent="."] +unique_name_in_owner = true +position = Vector2(100, 0) + +[node name="EndArea2D" type="Area2D" parent="EndPoint"] + +[node name="CollisionShape2D" type="CollisionShape2D" parent="EndPoint/EndArea2D"] +shape = SubResource("CircleShape2D_vx16l") + +[node name="EndSprite2D" type="Sprite2D" parent="EndPoint"] +unique_name_in_owner = true +material = SubResource("ShaderMaterial_r8oe2") +scale = Vector2(0.025, 0.025) +texture = ExtResource("2_qxkbt") + +[node name="EndSprite2D2" type="Sprite2D" parent="EndPoint"] +unique_name_in_owner = true +material = SubResource("ShaderMaterial_i3wlg") +scale = Vector2(0.025, 0.025) +texture = ExtResource("3_itlt2") + +[connection signal="mouse_entered" from="StartPoint/StartArea2D" to="." method="_on_start_area_2d_mouse_entered"] +[connection signal="mouse_exited" from="StartPoint/StartArea2D" to="." method="_on_start_area_2d_mouse_exited"] +[connection signal="mouse_entered" from="EndPoint/EndArea2D" to="." method="_on_end_area_2d_mouse_entered"] +[connection signal="mouse_exited" from="EndPoint/EndArea2D" to="." method="_on_end_area_2d_mouse_exited"] diff --git a/godot/scenes/control/control_point.tscn b/godot/scenes/control/control_point.tscn new file mode 100644 index 0000000..a5df925 --- /dev/null +++ b/godot/scenes/control/control_point.tscn @@ -0,0 +1,37 @@ +[gd_scene load_steps=8 format=3 uid="uid://dbiofgdsmekqi"] + +[ext_resource type="Script" path="res://src/control/control_point.gd" id="1_iu2b2"] +[ext_resource type="Texture2D" uid="uid://cwvu8b6n6ba6b" path="res://icons/font_awesome/circle-regular.svg" id="2_6i2pg"] +[ext_resource type="Shader" path="res://shaders/fill.gdshader" id="2_bxe8b"] +[ext_resource type="Texture2D" uid="uid://d4dxklmx6r8is" path="res://icons/font_awesome/circle-solid.svg" id="3_qylmn"] + +[sub_resource type="CircleShape2D" id="CircleShape2D_mps6y"] + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_2yy3u"] +shader = ExtResource("2_bxe8b") +shader_parameter/fill_color = Color(1, 1, 1, 1) + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_45gnb"] +shader = ExtResource("2_bxe8b") +shader_parameter/fill_color = Color(0, 0, 0, 1) + +[node name="ControlPoint" type="Node2D"] +script = ExtResource("1_iu2b2") + +[node name="Area2D" type="Area2D" parent="."] + +[node name="CollisionShape2D" type="CollisionShape2D" parent="Area2D"] +shape = SubResource("CircleShape2D_mps6y") + +[node name="Sprite2D" type="Sprite2D" parent="."] +material = SubResource("ShaderMaterial_2yy3u") +scale = Vector2(0.025, 0.025) +texture = ExtResource("3_qylmn") + +[node name="Sprite2D2" type="Sprite2D" parent="."] +material = SubResource("ShaderMaterial_45gnb") +scale = Vector2(0.025, 0.025) +texture = ExtResource("2_6i2pg") + +[connection signal="mouse_entered" from="Area2D" to="." method="_on_area_2d_mouse_entered"] +[connection signal="mouse_exited" from="Area2D" to="." method="_on_area_2d_mouse_exited"] diff --git a/godot/scenes/control/control_radial_gradient.tscn b/godot/scenes/control/control_radial_gradient.tscn new file mode 100644 index 0000000..ea48b01 --- /dev/null +++ b/godot/scenes/control/control_radial_gradient.tscn @@ -0,0 +1,103 @@ +[gd_scene load_steps=9 format=3 uid="uid://blrkftxov4tvd"] + +[ext_resource type="Script" path="res://src/control/control_radial_gradient.gd" id="1_tgq8f"] +[ext_resource type="Shader" path="res://shaders/fill.gdshader" id="2_0g4id"] +[ext_resource type="Texture2D" uid="uid://d4dxklmx6r8is" path="res://icons/font_awesome/circle-solid.svg" id="3_6ffsl"] +[ext_resource type="Texture2D" uid="uid://cwvu8b6n6ba6b" path="res://icons/font_awesome/circle-regular.svg" id="4_xvy6l"] + +[sub_resource type="CircleShape2D" id="CircleShape2D_vgdx4"] +radius = 12.0 + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_r8oe2"] +shader = ExtResource("2_0g4id") +shader_parameter/fill_color = Color(1, 1, 1, 1) + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_i3wlg"] +shader = ExtResource("2_0g4id") +shader_parameter/fill_color = Color(0, 0, 0, 1) + +[sub_resource type="CircleShape2D" id="CircleShape2D_vx16l"] + +[node name="ControlRadialGradient" type="Node2D"] +script = ExtResource("1_tgq8f") + +[node name="Line2D1" type="Line2D" parent="."] +unique_name_in_owner = true +points = PackedVector2Array(0, 0, 100, 0) +width = 5.0 +default_color = Color(1, 1, 1, 0.501961) + +[node name="Line2D2" type="Line2D" parent="."] +unique_name_in_owner = true +points = PackedVector2Array(0, 0, 0, -100) +width = 5.0 +default_color = Color(1, 1, 1, 0.501961) + +[node name="CenterPoint" type="Node2D" parent="."] +unique_name_in_owner = true + +[node name="CenterArea2D" type="Area2D" parent="CenterPoint"] + +[node name="CollisionShape2D" type="CollisionShape2D" parent="CenterPoint/CenterArea2D"] +shape = SubResource("CircleShape2D_vgdx4") + +[node name="CenterSprite2D" type="Sprite2D" parent="CenterPoint"] +unique_name_in_owner = true +material = SubResource("ShaderMaterial_r8oe2") +scale = Vector2(0.04, 0.04) +texture = ExtResource("3_6ffsl") + +[node name="CenterSprite2D2" type="Sprite2D" parent="CenterPoint"] +unique_name_in_owner = true +material = SubResource("ShaderMaterial_i3wlg") +scale = Vector2(0.025, 0.025) +texture = ExtResource("4_xvy6l") + +[node name="Handle1Point" type="Node2D" parent="."] +unique_name_in_owner = true +position = Vector2(100, 0) + +[node name="Handle1Area2D" type="Area2D" parent="Handle1Point"] + +[node name="CollisionShape2D" type="CollisionShape2D" parent="Handle1Point/Handle1Area2D"] +shape = SubResource("CircleShape2D_vx16l") + +[node name="Handle1Sprite2D" type="Sprite2D" parent="Handle1Point"] +unique_name_in_owner = true +material = SubResource("ShaderMaterial_r8oe2") +scale = Vector2(0.025, 0.025) +texture = ExtResource("3_6ffsl") + +[node name="Handle1Sprite2D2" type="Sprite2D" parent="Handle1Point"] +unique_name_in_owner = true +material = SubResource("ShaderMaterial_i3wlg") +scale = Vector2(0.025, 0.025) +texture = ExtResource("4_xvy6l") + +[node name="Handle2Point" type="Node2D" parent="."] +unique_name_in_owner = true +position = Vector2(0, -100) + +[node name="Handle2Area2D" type="Area2D" parent="Handle2Point"] + +[node name="CollisionShape2D" type="CollisionShape2D" parent="Handle2Point/Handle2Area2D"] +shape = SubResource("CircleShape2D_vx16l") + +[node name="Handle2Sprite2D" type="Sprite2D" parent="Handle2Point"] +unique_name_in_owner = true +material = SubResource("ShaderMaterial_r8oe2") +scale = Vector2(0.025, 0.025) +texture = ExtResource("3_6ffsl") + +[node name="Handle2Sprite2D2" type="Sprite2D" parent="Handle2Point"] +unique_name_in_owner = true +material = SubResource("ShaderMaterial_i3wlg") +scale = Vector2(0.025, 0.025) +texture = ExtResource("4_xvy6l") + +[connection signal="mouse_entered" from="CenterPoint/CenterArea2D" to="." method="_on_center_area_2d_mouse_entered"] +[connection signal="mouse_exited" from="CenterPoint/CenterArea2D" to="." method="_on_center_area_2d_mouse_exited"] +[connection signal="mouse_entered" from="Handle1Point/Handle1Area2D" to="." method="_on_handle_1_area_2d_mouse_entered"] +[connection signal="mouse_exited" from="Handle1Point/Handle1Area2D" to="." method="_on_handle_1_area_2d_mouse_exited"] +[connection signal="mouse_entered" from="Handle2Point/Handle2Area2D" to="." method="_on_handle_2_area_2d_mouse_entered"] +[connection signal="mouse_exited" from="Handle2Point/Handle2Area2D" to="." method="_on_handle_2_area_2d_mouse_exited"] diff --git a/godot/scenes/control/control_segment.tscn b/godot/scenes/control/control_segment.tscn new file mode 100644 index 0000000..0b6af35 --- /dev/null +++ b/godot/scenes/control/control_segment.tscn @@ -0,0 +1,14 @@ +[gd_scene load_steps=4 format=3 uid="uid://ctlbctut31mb8"] + +[ext_resource type="Shader" path="res://shaders/fill.gdshader" id="1_kfr7m"] +[ext_resource type="Script" path="res://src/control/control_segment.gd" id="1_vb88m"] + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_fcsnq"] +shader = ExtResource("1_kfr7m") +shader_parameter/fill_color = Color(1, 1, 1, 1) + +[node name="ControlSegment" type="Node2D"] +script = ExtResource("1_vb88m") + +[node name="Line2D" type="Line2D" parent="."] +material = SubResource("ShaderMaterial_fcsnq") diff --git a/godot/scenes/control/control_transform.tscn b/godot/scenes/control/control_transform.tscn new file mode 100644 index 0000000..b1368c8 --- /dev/null +++ b/godot/scenes/control/control_transform.tscn @@ -0,0 +1,307 @@ +[gd_scene load_steps=13 format=3 uid="uid://cd6xjiluvcmfe"] + +[ext_resource type="Script" path="res://src/control/control_transform.gd" id="1_7k5c8"] +[ext_resource type="Texture2D" uid="uid://x10k6c7p5yn2" path="res://icons/font_awesome/rotate-left-solid.svg" id="1_dn6gb"] +[ext_resource type="Shader" path="res://shaders/fill.gdshader" id="2_dxc0g"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_dvhqb"] +bg_color = Color(1, 1, 1, 0) +border_width_left = 2 +border_width_top = 2 +border_width_right = 2 +border_width_bottom = 2 +border_color = Color(0, 0, 0, 1) + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_jqsfc"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_okvfr"] +bg_color = Color(1, 1, 1, 1) +border_width_left = 2 +border_width_top = 2 +border_width_right = 2 +border_width_bottom = 2 +border_color = Color(0, 0, 0, 1) + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_1pu6h"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_pmy4g"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_1awu5"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_cbp70"] +bg_color = Color(1, 1, 1, 1) +border_width_left = 2 +border_width_top = 2 +border_width_right = 2 +border_width_bottom = 2 +border_color = Color(0, 0, 0, 1) + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_bj18v"] + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_qpuq3"] +shader = ExtResource("2_dxc0g") +shader_parameter/fill_color = Color(1, 1, 1, 1) + +[node name="ControlTransform" type="Panel"] +offset_right = 300.0 +offset_bottom = 200.0 +pivot_offset = Vector2(150, 100) +mouse_filter = 1 +mouse_default_cursor_shape = 13 +theme_override_styles/panel = SubResource("StyleBoxFlat_dvhqb") +script = ExtResource("1_7k5c8") + +[node name="TopControl" type="Panel" parent="."] +layout_mode = 1 +anchors_preset = 10 +anchor_right = 1.0 +offset_top = -6.0 +offset_bottom = 6.0 +grow_horizontal = 2 +mouse_filter = 1 +mouse_default_cursor_shape = 9 +theme_override_styles/panel = SubResource("StyleBoxEmpty_jqsfc") + +[node name="Panel" type="Panel" parent="TopControl"] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -6.0 +offset_top = -11.0 +offset_right = 6.0 +offset_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 1 +mouse_default_cursor_shape = 9 +theme_override_styles/panel = SubResource("StyleBoxFlat_okvfr") + +[node name="LeftControl" type="Panel" parent="."] +layout_mode = 1 +anchors_preset = 9 +anchor_bottom = 1.0 +offset_left = -6.0 +offset_right = 6.0 +grow_vertical = 2 +mouse_filter = 1 +mouse_default_cursor_shape = 10 +theme_override_styles/panel = SubResource("StyleBoxEmpty_1pu6h") + +[node name="Panel" type="Panel" parent="LeftControl"] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -11.0 +offset_top = -6.0 +offset_right = 1.0 +offset_bottom = 6.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 1 +mouse_default_cursor_shape = 10 +theme_override_styles/panel = SubResource("StyleBoxFlat_okvfr") + +[node name="RightControl" type="Panel" parent="."] +layout_mode = 1 +anchors_preset = 11 +anchor_left = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = -6.0 +offset_right = 6.0 +grow_horizontal = 0 +grow_vertical = 2 +mouse_filter = 1 +mouse_default_cursor_shape = 10 +theme_override_styles/panel = SubResource("StyleBoxEmpty_pmy4g") + +[node name="Panel" type="Panel" parent="RightControl"] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -1.0 +offset_top = -6.0 +offset_right = 11.0 +offset_bottom = 6.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 1 +mouse_default_cursor_shape = 10 +theme_override_styles/panel = SubResource("StyleBoxFlat_okvfr") + +[node name="BottomControl" type="Panel" parent="."] +layout_mode = 1 +anchors_preset = 12 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_top = -6.0 +offset_bottom = 6.0 +grow_horizontal = 2 +grow_vertical = 0 +mouse_filter = 1 +mouse_default_cursor_shape = 9 +theme_override_styles/panel = SubResource("StyleBoxEmpty_1awu5") + +[node name="Panel" type="Panel" parent="BottomControl"] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -6.0 +offset_top = -1.0 +offset_right = 6.0 +offset_bottom = 11.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 1 +mouse_default_cursor_shape = 9 +theme_override_styles/panel = SubResource("StyleBoxFlat_okvfr") + +[node name="TopLeftControl" type="Panel" parent="."] +layout_mode = 1 +offset_left = -11.0 +offset_top = -11.0 +offset_right = 1.0 +offset_bottom = 1.0 +mouse_filter = 1 +mouse_default_cursor_shape = 12 +theme_override_styles/panel = SubResource("StyleBoxFlat_cbp70") + +[node name="TopRightControl" type="Panel" parent="."] +layout_mode = 1 +anchors_preset = 1 +anchor_left = 1.0 +anchor_right = 1.0 +offset_left = -1.0 +offset_top = -11.0 +offset_right = 11.0 +offset_bottom = 1.0 +grow_horizontal = 0 +mouse_filter = 1 +mouse_default_cursor_shape = 11 +theme_override_styles/panel = SubResource("StyleBoxFlat_cbp70") + +[node name="BottomLeftControl" type="Panel" parent="."] +layout_mode = 1 +anchors_preset = 2 +anchor_top = 1.0 +anchor_bottom = 1.0 +offset_left = -11.0 +offset_top = -1.0 +offset_right = 1.0 +offset_bottom = 11.0 +grow_vertical = 0 +mouse_filter = 1 +mouse_default_cursor_shape = 11 +theme_override_styles/panel = SubResource("StyleBoxFlat_cbp70") + +[node name="BottomRightControl" type="Panel" parent="."] +layout_mode = 1 +anchors_preset = 3 +anchor_left = 1.0 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = -1.0 +offset_top = -1.0 +offset_right = 11.0 +offset_bottom = 11.0 +grow_horizontal = 0 +grow_vertical = 0 +mouse_filter = 1 +mouse_default_cursor_shape = 12 +theme_override_styles/panel = SubResource("StyleBoxFlat_cbp70") + +[node name="RotationControl" type="Panel" parent="."] +custom_minimum_size = Vector2(24, 24) +layout_mode = 1 +anchors_preset = 5 +anchor_left = 0.5 +anchor_right = 0.5 +offset_left = -12.0 +offset_top = -38.0 +offset_right = 12.0 +offset_bottom = -14.0 +grow_horizontal = 2 +mouse_filter = 1 +mouse_default_cursor_shape = 2 +theme_override_styles/panel = SubResource("StyleBoxEmpty_bj18v") + +[node name="Sprite2D2" type="Sprite2D" parent="RotationControl"] +position = Vector2(10, 10) +scale = Vector2(0.04, 0.04) +texture = ExtResource("1_dn6gb") + +[node name="Sprite2D3" type="Sprite2D" parent="RotationControl"] +position = Vector2(14, 10) +scale = Vector2(0.04, 0.04) +texture = ExtResource("1_dn6gb") + +[node name="Sprite2D4" type="Sprite2D" parent="RotationControl"] +position = Vector2(10, 14) +scale = Vector2(0.04, 0.04) +texture = ExtResource("1_dn6gb") + +[node name="Sprite2D5" type="Sprite2D" parent="RotationControl"] +position = Vector2(14, 14) +scale = Vector2(0.04, 0.04) +texture = ExtResource("1_dn6gb") + +[node name="Sprite2D6" type="Sprite2D" parent="RotationControl"] +position = Vector2(12, 10) +scale = Vector2(0.04, 0.04) +texture = ExtResource("1_dn6gb") + +[node name="Sprite2D7" type="Sprite2D" parent="RotationControl"] +position = Vector2(10, 12) +scale = Vector2(0.04, 0.04) +texture = ExtResource("1_dn6gb") + +[node name="Sprite2D8" type="Sprite2D" parent="RotationControl"] +position = Vector2(14, 12) +scale = Vector2(0.04, 0.04) +texture = ExtResource("1_dn6gb") + +[node name="Sprite2D9" type="Sprite2D" parent="RotationControl"] +position = Vector2(12, 14) +scale = Vector2(0.04, 0.04) +texture = ExtResource("1_dn6gb") + +[node name="Sprite2D" type="Sprite2D" parent="RotationControl"] +material = SubResource("ShaderMaterial_qpuq3") +position = Vector2(12, 12) +scale = Vector2(0.04, 0.04) +texture = ExtResource("1_dn6gb") + +[connection signal="mouse_entered" from="." to="." method="_on_mouse_entered"] +[connection signal="mouse_exited" from="." to="." method="_on_mouse_exited"] +[connection signal="mouse_entered" from="TopControl" to="." method="_on_top_control_mouse_entered"] +[connection signal="mouse_exited" from="TopControl" to="." method="_on_top_control_mouse_exited"] +[connection signal="mouse_entered" from="LeftControl" to="." method="_on_left_control_mouse_entered"] +[connection signal="mouse_exited" from="LeftControl" to="." method="_on_left_control_mouse_exited"] +[connection signal="mouse_entered" from="RightControl" to="." method="_on_right_control_mouse_entered"] +[connection signal="mouse_exited" from="RightControl" to="." method="_on_right_control_mouse_exited"] +[connection signal="mouse_entered" from="BottomControl" to="." method="_on_bottom_control_mouse_entered"] +[connection signal="mouse_exited" from="BottomControl" to="." method="_on_bottom_control_mouse_exited"] +[connection signal="mouse_entered" from="TopLeftControl" to="." method="_on_top_left_control_mouse_entered"] +[connection signal="mouse_exited" from="TopLeftControl" to="." method="_on_top_left_control_mouse_exited"] +[connection signal="mouse_entered" from="TopRightControl" to="." method="_on_top_right_control_mouse_entered"] +[connection signal="mouse_exited" from="TopRightControl" to="." method="_on_top_right_control_mouse_exited"] +[connection signal="mouse_entered" from="BottomLeftControl" to="." method="_on_bottom_left_control_mouse_entered"] +[connection signal="mouse_exited" from="BottomLeftControl" to="." method="_on_bottom_left_control_mouse_exited"] +[connection signal="mouse_entered" from="BottomRightControl" to="." method="_on_bottom_right_control_mouse_entered"] +[connection signal="mouse_exited" from="BottomRightControl" to="." method="_on_bottom_right_control_mouse_exited"] +[connection signal="mouse_entered" from="RotationControl" to="." method="_on_rotation_control_mouse_entered"] +[connection signal="mouse_exited" from="RotationControl" to="." method="_on_rotation_control_mouse_exited"] diff --git a/godot/scenes/node/gradient_editor.tscn b/godot/scenes/node/gradient_editor.tscn new file mode 100644 index 0000000..dc16002 --- /dev/null +++ b/godot/scenes/node/gradient_editor.tscn @@ -0,0 +1,66 @@ +[gd_scene load_steps=4 format=3 uid="uid://bdyvforlk3gff"] + +[ext_resource type="Script" path="res://src/node/gradient_editor.gd" id="1_lkn08"] +[ext_resource type="Shader" path="res://shaders/linear_gradient_checker_alpha.gdshader" id="2_octm4"] + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_pppqu"] +shader = ExtResource("2_octm4") +shader_parameter/document_size = Vector2(240, 40) +shader_parameter/document_scale = 1.0 + +[node name="GradientEditor" type="VBoxContainer"] +offset_right = 350.0 +offset_bottom = 650.0 +script = ExtResource("1_lkn08") + +[node name="Panel" type="Control" parent="."] +custom_minimum_size = Vector2(0, 64) +layout_mode = 2 + +[node name="ColorRect" type="ColorRect" parent="Panel"] +unique_name_in_owner = true +material = SubResource("ShaderMaterial_pppqu") +layout_mode = 0 +offset_left = 12.0 +offset_top = 12.0 +offset_right = 302.0 +offset_bottom = 52.0 + +[node name="PlusButton" type="Button" parent="Panel"] +layout_mode = 0 +offset_left = 310.0 +offset_top = 12.0 +offset_right = 350.0 +offset_bottom = 32.0 + +[node name="Label" type="Label" parent="Panel/PlusButton"] +layout_mode = 0 +offset_right = 40.0 +offset_bottom = 23.0 +text = "+" +horizontal_alignment = 1 + +[node name="MinusButton" type="Button" parent="Panel"] +layout_mode = 0 +offset_left = 310.0 +offset_top = 32.0 +offset_right = 350.0 +offset_bottom = 52.0 + +[node name="Label" type="Label" parent="Panel/MinusButton"] +layout_mode = 0 +offset_top = -8.0 +offset_right = 40.0 +offset_bottom = 26.0 +theme_override_font_sizes/font_size = 24 +text = "-" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="ColorPicker" type="ColorPicker" parent="."] +unique_name_in_owner = true +layout_mode = 2 + +[connection signal="pressed" from="Panel/PlusButton" to="." method="_on_plus_button_pressed"] +[connection signal="pressed" from="Panel/MinusButton" to="." method="_on_minus_button_pressed"] +[connection signal="color_changed" from="ColorPicker" to="." method="_on_color_picker_color_changed"] diff --git a/godot/scenes/node/gradient_editor_color_arrow.tscn b/godot/scenes/node/gradient_editor_color_arrow.tscn new file mode 100644 index 0000000..e150328 --- /dev/null +++ b/godot/scenes/node/gradient_editor_color_arrow.tscn @@ -0,0 +1,37 @@ +[gd_scene load_steps=6 format=3 uid="uid://dmry8668lobcy"] + +[ext_resource type="Script" path="res://src/node/gradient_editor_color_arrow.gd" id="1_awnen"] +[ext_resource type="Texture2D" uid="uid://cwtuhfpnanbh8" path="res://icons/gradient-editor-color-arrow.svg" id="1_dexvh"] +[ext_resource type="Shader" path="res://shaders/fill.gdshader" id="1_smwwm"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_y3k24"] + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_1ntfu"] +shader = ExtResource("1_smwwm") +shader_parameter/fill_color = Color(0.0980392, 0.0980392, 0.0980392, 1) + +[node name="GradientEditorColorArrow" type="Panel"] +offset_left = -12.0 +offset_right = 12.0 +offset_bottom = 48.0 +theme_override_styles/panel = SubResource("StyleBoxEmpty_y3k24") +script = ExtResource("1_awnen") + +[node name="Sprite2D" type="Sprite2D" parent="."] +unique_name_in_owner = true +material = SubResource("ShaderMaterial_1ntfu") +position = Vector2(12, 32) +scale = Vector2(0.05, 0.05) +texture = ExtResource("1_dexvh") + +[node name="ColorRect" type="ColorRect" parent="."] +unique_name_in_owner = true +layout_mode = 0 +offset_left = 4.0 +offset_top = 30.0 +offset_right = 20.0 +offset_bottom = 46.0 +mouse_filter = 2 + +[connection signal="mouse_entered" from="." to="." method="_on_mouse_entered"] +[connection signal="mouse_exited" from="." to="." method="_on_mouse_exited"] diff --git a/godot/scenes/node/layer_list_item.tscn b/godot/scenes/node/layer_list_item.tscn new file mode 100644 index 0000000..f4274de --- /dev/null +++ b/godot/scenes/node/layer_list_item.tscn @@ -0,0 +1,365 @@ +[gd_scene load_steps=44 format=3 uid="uid://bsh7inpct6qfa"] + +[ext_resource type="Script" path="res://src/node/layer_list_item.gd" id="1_bj6dd"] +[ext_resource type="Shader" path="res://shaders/fill.gdshader" id="2_klhf3"] +[ext_resource type="Texture2D" uid="uid://cp43s5pjuigfc" path="res://icons/font_awesome/eye-solid.svg" id="3_leknr"] +[ext_resource type="Texture2D" uid="uid://bs6y5xvpid1hl" path="res://icons/font_awesome/eye-slash-solid.svg" id="4_w4jag"] +[ext_resource type="Texture2D" uid="uid://07j3j13jje5v" path="res://icons/font_awesome/pen-nib-solid.svg" id="5_ou1hq"] +[ext_resource type="Texture2D" uid="uid://yof4lmve8kdo" path="res://icons/font_awesome/folder-open-regular.svg" id="6_k6cky"] +[ext_resource type="Texture2D" uid="uid://bgaugxuhl5lvm" path="res://icons/font_awesome/lock-solid.svg" id="6_kfw3k"] +[ext_resource type="Texture2D" uid="uid://dbdfmeeypughg" path="res://icons/font_awesome/folder-regular.svg" id="7_ncive"] +[ext_resource type="Texture2D" uid="uid://bihym07yhfwvb" path="res://icons/font_awesome/caret-down-solid.svg" id="8_vkygf"] +[ext_resource type="Texture2D" uid="uid://1jifl7a7wmah" path="res://icons/font_awesome/caret-right-solid.svg" id="9_fb6nd"] +[ext_resource type="Shader" path="res://shaders/texture_checker_alpha.gdshader" id="10_2ibs7"] +[ext_resource type="Texture2D" uid="uid://bkls8jpsc3ps5" path="res://icons/union.svg" id="10_g20d4"] +[ext_resource type="Texture2D" uid="uid://c8eyxuhtxxx2w" path="res://icons/font_awesome/fill-solid.svg" id="10_nbeeb"] +[ext_resource type="Texture2D" uid="uid://esb812dxjhx3" path="res://icons/diff.svg" id="11_ux43y"] +[ext_resource type="Texture2D" uid="uid://delwiq2ud4o5b" path="res://icons/intersect.svg" id="12_cu8tf"] +[ext_resource type="Texture2D" uid="uid://bsxiknl4vq61y" path="res://icons/xor.svg" id="13_ivlsh"] +[ext_resource type="Texture2D" uid="uid://b4pppiaal50ew" path="res://icons/font_awesome/circle-question-solid.svg" id="17_m778j"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_pyity"] +bg_color = Color(0.2, 0.2, 0.2, 1) + +[sub_resource type="StyleBoxLine" id="StyleBoxLine_6gion"] +color = Color(0.266667, 0.266667, 0.266667, 1) + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_xr2hg"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_mh0ym"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_nxfcb"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_56cox"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_pi05j"] + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_etyw0"] +shader = ExtResource("2_klhf3") +shader_parameter/fill_color = Color(0.4, 0.4, 0.4, 1) + +[sub_resource type="StyleBoxLine" id="StyleBoxLine_hg1ec"] +color = Color(0.266667, 0.266667, 0.266667, 1) +vertical = true + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_f1jh4"] + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_lqmmd"] +shader = ExtResource("2_klhf3") +shader_parameter/fill_color = Color(0.8, 0.8, 0.8, 1) + +[sub_resource type="StyleBoxLine" id="StyleBoxLine_hgco8"] +color = Color(0.266667, 0.266667, 0.266667, 1) +vertical = true + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_6qubt"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 5.0 +bg_color = Color(0.6, 0.6, 0.6, 0) + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_ylncw"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_fohug"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_wwj3h"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_3a6jb"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_ye4cw"] + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_cuuu8"] +shader = ExtResource("2_klhf3") +shader_parameter/fill_color = Color(0.8, 0.8, 0.8, 1) + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_rjxm6"] +shader = ExtResource("10_2ibs7") +shader_parameter/document_size = Vector2(480, 720) +shader_parameter/document_scale = 1.0 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_cax14"] +bg_color = Color(0.6, 0.6, 0.6, 0) + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_y3ecf"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_b5gnl"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_3srct"] + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_dlgp3"] +shader = ExtResource("2_klhf3") +shader_parameter/fill_color = Color(1, 1, 1, 1) + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_c6wcm"] +bg_color = Color(0, 0, 0, 0.784314) + +[node name="LayerListItem" type="PanelContainer"] +custom_minimum_size = Vector2(0, 48) +offset_right = 280.0 +offset_bottom = 48.0 +size_flags_horizontal = 3 +mouse_filter = 1 +theme_override_styles/panel = SubResource("StyleBoxFlat_pyity") +script = ExtResource("1_bj6dd") + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +layout_mode = 2 +theme_override_constants/separation = 0 + +[node name="HSeparator" type="HSeparator" parent="VBoxContainer"] +layout_mode = 2 +mouse_filter = 1 +theme_override_constants/separation = 1 +theme_override_styles/separator = SubResource("StyleBoxLine_6gion") + +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer"] +layout_mode = 2 +size_flags_vertical = 3 +theme_override_constants/separation = 0 + +[node name="EyeButton" type="Button" parent="VBoxContainer/HBoxContainer"] +unique_name_in_owner = true +custom_minimum_size = Vector2(24, 24) +layout_mode = 2 +focus_mode = 0 +mouse_filter = 1 +theme_override_styles/focus = SubResource("StyleBoxEmpty_xr2hg") +theme_override_styles/disabled = SubResource("StyleBoxEmpty_mh0ym") +theme_override_styles/hover = SubResource("StyleBoxEmpty_nxfcb") +theme_override_styles/pressed = SubResource("StyleBoxEmpty_56cox") +theme_override_styles/normal = SubResource("StyleBoxEmpty_pi05j") + +[node name="EyeOpen" type="Sprite2D" parent="VBoxContainer/HBoxContainer/EyeButton"] +material = SubResource("ShaderMaterial_etyw0") +position = Vector2(12, 24) +scale = Vector2(0.02, 0.02) +texture = ExtResource("3_leknr") + +[node name="EyeSlash" type="Sprite2D" parent="VBoxContainer/HBoxContainer/EyeButton"] +material = SubResource("ShaderMaterial_etyw0") +position = Vector2(12, 24) +scale = Vector2(0.02, 0.02) +texture = ExtResource("4_w4jag") + +[node name="VSeparator" type="VSeparator" parent="VBoxContainer/HBoxContainer"] +layout_mode = 2 +mouse_filter = 1 +theme_override_styles/separator = SubResource("StyleBoxLine_hg1ec") + +[node name="PenPanel" type="Panel" parent="VBoxContainer/HBoxContainer"] +unique_name_in_owner = true +visible = false +custom_minimum_size = Vector2(24, 24) +layout_mode = 2 +mouse_filter = 1 +theme_override_styles/panel = SubResource("StyleBoxEmpty_f1jh4") + +[node name="Sprite2D" type="Sprite2D" parent="VBoxContainer/HBoxContainer/PenPanel"] +material = SubResource("ShaderMaterial_lqmmd") +position = Vector2(12, 24) +scale = Vector2(0.025, 0.025) +texture = ExtResource("5_ou1hq") + +[node name="LockedPanel" type="Panel" parent="VBoxContainer/HBoxContainer"] +unique_name_in_owner = true +custom_minimum_size = Vector2(24, 24) +layout_mode = 2 +mouse_filter = 1 +theme_override_styles/panel = SubResource("StyleBoxEmpty_f1jh4") + +[node name="Sprite2D" type="Sprite2D" parent="VBoxContainer/HBoxContainer/LockedPanel"] +material = SubResource("ShaderMaterial_lqmmd") +position = Vector2(12, 24) +scale = Vector2(0.025, 0.025) +texture = ExtResource("6_kfw3k") + +[node name="VSeparator2" type="VSeparator" parent="VBoxContainer/HBoxContainer"] +layout_mode = 2 +mouse_filter = 1 +theme_override_styles/separator = SubResource("StyleBoxLine_hgco8") + +[node name="Spacer" type="ColorRect" parent="VBoxContainer/HBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +mouse_filter = 1 +color = Color(1, 1, 1, 0) + +[node name="Padding" type="PanelContainer" parent="VBoxContainer/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +mouse_filter = 1 +theme_override_styles/panel = SubResource("StyleBoxFlat_6qubt") + +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/HBoxContainer/Padding"] +layout_mode = 2 +theme_override_constants/separation = 0 + +[node name="ClippingRect" type="ColorRect" parent="VBoxContainer/HBoxContainer/Padding/HBoxContainer"] +unique_name_in_owner = true +custom_minimum_size = Vector2(4, 0) +layout_mode = 2 +mouse_filter = 1 +color = Color(1, 0.501961, 0.768627, 1) + +[node name="CollapseButton" type="Button" parent="VBoxContainer/HBoxContainer/Padding/HBoxContainer"] +unique_name_in_owner = true +custom_minimum_size = Vector2(24, 0) +layout_mode = 2 +focus_mode = 0 +mouse_filter = 1 +theme_override_styles/focus = SubResource("StyleBoxEmpty_ylncw") +theme_override_styles/disabled = SubResource("StyleBoxEmpty_fohug") +theme_override_styles/hover = SubResource("StyleBoxEmpty_wwj3h") +theme_override_styles/pressed = SubResource("StyleBoxEmpty_3a6jb") +theme_override_styles/normal = SubResource("StyleBoxEmpty_ye4cw") + +[node name="FolderOpen" type="Sprite2D" parent="VBoxContainer/HBoxContainer/Padding/HBoxContainer/CollapseButton"] +material = SubResource("ShaderMaterial_cuuu8") +position = Vector2(12, 20) +scale = Vector2(0.7, 0.7) +texture = ExtResource("6_k6cky") + +[node name="FolderClose" type="Sprite2D" parent="VBoxContainer/HBoxContainer/Padding/HBoxContainer/CollapseButton"] +visible = false +material = SubResource("ShaderMaterial_cuuu8") +position = Vector2(12, 20) +scale = Vector2(0.7, 0.7) +texture = ExtResource("7_ncive") + +[node name="CaretOpen" type="Sprite2D" parent="VBoxContainer/HBoxContainer/Padding/HBoxContainer/CollapseButton"] +visible = false +material = SubResource("ShaderMaterial_cuuu8") +position = Vector2(12, 20) +scale = Vector2(0.035, 0.035) +texture = ExtResource("8_vkygf") + +[node name="CaretClose" type="Sprite2D" parent="VBoxContainer/HBoxContainer/Padding/HBoxContainer/CollapseButton"] +visible = false +material = SubResource("ShaderMaterial_cuuu8") +position = Vector2(12, 20) +scale = Vector2(0.035, 0.035) +texture = ExtResource("9_fb6nd") + +[node name="FillMark" type="Sprite2D" parent="VBoxContainer/HBoxContainer/Padding/HBoxContainer/CollapseButton"] +visible = false +material = SubResource("ShaderMaterial_cuuu8") +position = Vector2(12, 20) +scale = Vector2(0.03, 0.03) +texture = ExtResource("10_nbeeb") + +[node name="Thumbnail" type="Panel" parent="VBoxContainer/HBoxContainer/Padding/HBoxContainer"] +unique_name_in_owner = true +custom_minimum_size = Vector2(38, 38) +layout_mode = 2 +mouse_filter = 1 + +[node name="ThumbnailRect" type="ColorRect" parent="VBoxContainer/HBoxContainer/Padding/HBoxContainer/Thumbnail"] +texture_filter = 4 +material = SubResource("ShaderMaterial_rjxm6") +layout_mode = 0 +offset_right = 24.0 +offset_bottom = 38.0 +mouse_filter = 1 + +[node name="Spacer" type="ColorRect" parent="VBoxContainer/HBoxContainer/Padding/HBoxContainer"] +custom_minimum_size = Vector2(8, 0) +layout_mode = 2 +mouse_filter = 1 +color = Color(1, 1, 1, 0) + +[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/HBoxContainer/Padding/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/HBoxContainer/Padding/HBoxContainer/VBoxContainer"] +layout_mode = 2 +theme_override_constants/separation = 4 + +[node name="BlendModeText" type="Label" parent="VBoxContainer/HBoxContainer/Padding/HBoxContainer/VBoxContainer/HBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_colors/font_color = Color(0.8, 0.8, 0.8, 1) +theme_override_font_sizes/font_size = 10 +text = "通常" + +[node name="AlphaText" type="Label" parent="VBoxContainer/HBoxContainer/Padding/HBoxContainer/VBoxContainer/HBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +theme_override_colors/font_color = Color(0.8, 0.8, 0.8, 1) +theme_override_font_sizes/font_size = 10 +text = "100%" + +[node name="BooleanPanel" type="Panel" parent="VBoxContainer/HBoxContainer/Padding/HBoxContainer/VBoxContainer/HBoxContainer"] +unique_name_in_owner = true +custom_minimum_size = Vector2(14, 0) +layout_mode = 2 +mouse_filter = 2 + +[node name="Union" type="Sprite2D" parent="VBoxContainer/HBoxContainer/Padding/HBoxContainer/VBoxContainer/HBoxContainer/BooleanPanel"] +position = Vector2(7, 7) +scale = Vector2(0.5, 0.5) +texture = ExtResource("10_g20d4") + +[node name="Diff" type="Sprite2D" parent="VBoxContainer/HBoxContainer/Padding/HBoxContainer/VBoxContainer/HBoxContainer/BooleanPanel"] +visible = false +position = Vector2(7, 7) +scale = Vector2(0.5, 0.5) +texture = ExtResource("11_ux43y") + +[node name="Intersect" type="Sprite2D" parent="VBoxContainer/HBoxContainer/Padding/HBoxContainer/VBoxContainer/HBoxContainer/BooleanPanel"] +visible = false +position = Vector2(7, 7) +scale = Vector2(0.5, 0.5) +texture = ExtResource("12_cu8tf") + +[node name="Xor" type="Sprite2D" parent="VBoxContainer/HBoxContainer/Padding/HBoxContainer/VBoxContainer/HBoxContainer/BooleanPanel"] +visible = false +position = Vector2(7, 7) +scale = Vector2(0.5, 0.5) +texture = ExtResource("13_ivlsh") + +[node name="NameText" type="LineEdit" parent="VBoxContainer/HBoxContainer/Padding/HBoxContainer/VBoxContainer"] +unique_name_in_owner = true +auto_translate_mode = 2 +layout_mode = 2 +size_flags_horizontal = 3 +mouse_filter = 1 +theme_override_font_sizes/font_size = 12 +theme_override_styles/focus = SubResource("StyleBoxFlat_cax14") +theme_override_styles/read_only = SubResource("StyleBoxEmpty_y3ecf") +theme_override_styles/normal = SubResource("StyleBoxEmpty_b5gnl") +editable = false +context_menu_enabled = false +middle_mouse_paste_enabled = false +selecting_enabled = false +drag_and_drop_selection_enabled = false + +[node name="MissingPanel" type="Panel" parent="VBoxContainer/HBoxContainer/Padding/HBoxContainer"] +unique_name_in_owner = true +custom_minimum_size = Vector2(32, 0) +layout_mode = 2 +tooltip_text = "TOOLTIP_MATRERIAL_DELETED" +mouse_filter = 1 +theme_override_styles/panel = SubResource("StyleBoxEmpty_3srct") + +[node name="Sprite2D" type="Sprite2D" parent="VBoxContainer/HBoxContainer/Padding/HBoxContainer/MissingPanel"] +material = SubResource("ShaderMaterial_dlgp3") +position = Vector2(16, 19) +scale = Vector2(0.5, 0.5) +texture = ExtResource("17_m778j") + +[node name="DraggingtPanel" type="Panel" parent="."] +visible = false +layout_mode = 2 +mouse_filter = 2 +theme_override_styles/panel = SubResource("StyleBoxFlat_c6wcm") + +[connection signal="mouse_entered" from="." to="." method="_on_mouse_entered"] +[connection signal="mouse_exited" from="." to="." method="_on_mouse_exited"] +[connection signal="button_up" from="VBoxContainer/HBoxContainer/EyeButton" to="." method="_on_eye_button_button_up"] +[connection signal="button_up" from="VBoxContainer/HBoxContainer/Padding/HBoxContainer/CollapseButton" to="." method="_on_collapse_button_button_up"] +[connection signal="text_changed" from="VBoxContainer/HBoxContainer/Padding/HBoxContainer/VBoxContainer/NameText" to="." method="_on_line_edit_text_changed"] +[connection signal="text_submitted" from="VBoxContainer/HBoxContainer/Padding/HBoxContainer/VBoxContainer/NameText" to="." method="_on_line_edit_text_submitted"] diff --git a/godot/scenes/node/linear_gradient_picker.tscn b/godot/scenes/node/linear_gradient_picker.tscn new file mode 100644 index 0000000..81dfecc --- /dev/null +++ b/godot/scenes/node/linear_gradient_picker.tscn @@ -0,0 +1,122 @@ +[gd_scene load_steps=3 format=3 uid="uid://djow8u4pxw5sl"] + +[ext_resource type="Script" path="res://src/node/linear_gradient_picker.gd" id="1_6x5uh"] +[ext_resource type="PackedScene" uid="uid://bdyvforlk3gff" path="res://scenes/node/gradient_editor.tscn" id="1_8qxkh"] + +[node name="LinearGradientPicker" type="Popup"] +size = Vector2i(350, 760) +visible = true +max_size = Vector2i(350, 32768) +script = ExtResource("1_6x5uh") + +[node name="PanelContainer" type="PanelContainer" parent="."] +custom_minimum_size = Vector2(350, 0) +offset_right = 350.0 +offset_bottom = 757.0 + +[node name="VBoxContainer" type="VBoxContainer" parent="PanelContainer"] +layout_mode = 2 + +[node name="VBoxContainer" type="VBoxContainer" parent="PanelContainer/VBoxContainer"] +layout_mode = 2 + +[node name="Padding" type="Control" parent="PanelContainer/VBoxContainer/VBoxContainer"] +layout_mode = 2 + +[node name="HBoxContainer" type="HBoxContainer" parent="PanelContainer/VBoxContainer/VBoxContainer"] +layout_mode = 2 + +[node name="Padding" type="Control" parent="PanelContainer/VBoxContainer/VBoxContainer/HBoxContainer"] +custom_minimum_size = Vector2(8, 0) +layout_mode = 2 + +[node name="Label" type="Label" parent="PanelContainer/VBoxContainer/VBoxContainer/HBoxContainer"] +layout_mode = 2 +text = "LINEAR_GRADIENT_START_POINT" + +[node name="HBoxContainer" type="HBoxContainer" parent="PanelContainer/VBoxContainer/VBoxContainer/HBoxContainer"] +layout_mode = 2 + +[node name="Label" type="Label" parent="PanelContainer/VBoxContainer/VBoxContainer/HBoxContainer/HBoxContainer"] +layout_mode = 2 +text = "x: " + +[node name="StartX" type="SpinBox" parent="PanelContainer/VBoxContainer/VBoxContainer/HBoxContainer/HBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +allow_greater = true +allow_lesser = true + +[node name="Label2" type="Label" parent="PanelContainer/VBoxContainer/VBoxContainer/HBoxContainer/HBoxContainer"] +layout_mode = 2 +text = "y: " + +[node name="StartY" type="SpinBox" parent="PanelContainer/VBoxContainer/VBoxContainer/HBoxContainer/HBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +allow_greater = true +allow_lesser = true + +[node name="HBoxContainer2" type="HBoxContainer" parent="PanelContainer/VBoxContainer/VBoxContainer"] +layout_mode = 2 + +[node name="Padding" type="Control" parent="PanelContainer/VBoxContainer/VBoxContainer/HBoxContainer2"] +custom_minimum_size = Vector2(8, 0) +layout_mode = 2 + +[node name="Label" type="Label" parent="PanelContainer/VBoxContainer/VBoxContainer/HBoxContainer2"] +layout_mode = 2 +text = "LINEAR_GRADIENT_END_POINT" + +[node name="HBoxContainer" type="HBoxContainer" parent="PanelContainer/VBoxContainer/VBoxContainer/HBoxContainer2"] +layout_mode = 2 + +[node name="Label" type="Label" parent="PanelContainer/VBoxContainer/VBoxContainer/HBoxContainer2/HBoxContainer"] +layout_mode = 2 +text = "x: " + +[node name="EndX" type="SpinBox" parent="PanelContainer/VBoxContainer/VBoxContainer/HBoxContainer2/HBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +allow_greater = true +allow_lesser = true + +[node name="Label2" type="Label" parent="PanelContainer/VBoxContainer/VBoxContainer/HBoxContainer2/HBoxContainer"] +layout_mode = 2 +text = "y: " + +[node name="EndY" type="SpinBox" parent="PanelContainer/VBoxContainer/VBoxContainer/HBoxContainer2/HBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +allow_greater = true +allow_lesser = true + +[node name="HBoxContainer3" type="HBoxContainer" parent="PanelContainer/VBoxContainer/VBoxContainer"] +layout_mode = 2 + +[node name="Padding" type="Control" parent="PanelContainer/VBoxContainer/VBoxContainer/HBoxContainer3"] +custom_minimum_size = Vector2(8, 0) +layout_mode = 2 + +[node name="EditButton" type="Button" parent="PanelContainer/VBoxContainer/VBoxContainer/HBoxContainer3"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "LINEAR_GRADIENT_EDIT" + +[node name="Padding2" type="Control" parent="PanelContainer/VBoxContainer/VBoxContainer/HBoxContainer3"] +custom_minimum_size = Vector2(8, 0) +layout_mode = 2 + +[node name="Padding2" type="Control" parent="PanelContainer/VBoxContainer/VBoxContainer"] +layout_mode = 2 + +[node name="GradientEditor" parent="PanelContainer/VBoxContainer" instance=ExtResource("1_8qxkh")] +unique_name_in_owner = true +layout_mode = 2 + +[connection signal="popup_hide" from="." to="." method="_on_popup_hide"] +[connection signal="value_changed" from="PanelContainer/VBoxContainer/VBoxContainer/HBoxContainer/HBoxContainer/StartX" to="." method="_on_start_x_value_changed"] +[connection signal="value_changed" from="PanelContainer/VBoxContainer/VBoxContainer/HBoxContainer/HBoxContainer/StartY" to="." method="_on_start_y_value_changed"] +[connection signal="value_changed" from="PanelContainer/VBoxContainer/VBoxContainer/HBoxContainer2/HBoxContainer/EndX" to="." method="_on_end_x_value_changed"] +[connection signal="value_changed" from="PanelContainer/VBoxContainer/VBoxContainer/HBoxContainer2/HBoxContainer/EndY" to="." method="_on_end_y_value_changed"] +[connection signal="pressed" from="PanelContainer/VBoxContainer/VBoxContainer/HBoxContainer3/EditButton" to="." method="_on_edit_button_pressed"] diff --git a/godot/scenes/node/list_drop_cursor.tscn b/godot/scenes/node/list_drop_cursor.tscn new file mode 100644 index 0000000..6a00e14 --- /dev/null +++ b/godot/scenes/node/list_drop_cursor.tscn @@ -0,0 +1,35 @@ +[gd_scene load_steps=2 format=3 uid="uid://83wedgkgqa8o"] + +[ext_resource type="Script" path="res://src/node/list_drop_cursor.gd" id="1_gtl4h"] + +[node name="ListDropCursor" type="BoxContainer"] +anchors_preset = 14 +anchor_top = 0.5 +anchor_right = 1.0 +anchor_bottom = 0.5 +offset_top = -2.0 +offset_bottom = -2.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_vertical = 4 +mouse_filter = 2 +script = ExtResource("1_gtl4h") + +[node name="Control" type="Control" parent="."] +layout_mode = 2 +size_flags_horizontal = 3 +mouse_filter = 2 + +[node name="ColorRect" type="ColorRect" parent="Control"] +z_index = 10 +layout_mode = 1 +anchors_preset = 4 +anchor_top = 0.5 +anchor_bottom = 0.5 +offset_top = -2.0 +offset_right = 1132.0 +offset_bottom = 2.0 +grow_vertical = 2 +size_flags_horizontal = 3 +mouse_filter = 2 +color = Color(0.501961, 0.8, 0.701961, 1) diff --git a/godot/scenes/node/material_list_item.tscn b/godot/scenes/node/material_list_item.tscn new file mode 100644 index 0000000..3c53053 --- /dev/null +++ b/godot/scenes/node/material_list_item.tscn @@ -0,0 +1,144 @@ +[gd_scene load_steps=18 format=3 uid="uid://clsrljwk4jsph"] + +[ext_resource type="Script" path="res://src/node/material_list_item.gd" id="1_qgde6"] +[ext_resource type="Shader" path="res://shaders/fill_checker_alpha.gdshader" id="2_h5yow"] +[ext_resource type="Shader" path="res://shaders/linear_gradient_checker_alpha.gdshader" id="3_0m1d3"] +[ext_resource type="Shader" path="res://shaders/radial_gradient_checker_alpha.gdshader" id="4_cb2k2"] +[ext_resource type="PackedScene" uid="uid://djow8u4pxw5sl" path="res://scenes/node/linear_gradient_picker.tscn" id="4_iyron"] +[ext_resource type="PackedScene" uid="uid://bl0lqn8s0d1d3" path="res://scenes/node/radial_gradient_picker.tscn" id="6_5vs5c"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_pyity"] + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_5rytv"] +shader = ExtResource("2_h5yow") +shader_parameter/fill_color = Color(1, 1, 1, 1) +shader_parameter/document_size = Vector2(64, 24) +shader_parameter/document_scale = 1.0 + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_smcwm"] +shader = ExtResource("3_0m1d3") +shader_parameter/document_size = Vector2(64, 24) +shader_parameter/document_scale = 1.0 + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_urnwi"] +shader = ExtResource("4_cb2k2") +shader_parameter/document_size = Vector2(64, 24) +shader_parameter/document_scale = 1.0 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_6qubt"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 +bg_color = Color(0.6, 0.6, 0.6, 0) + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_hsq3x"] +shader = ExtResource("2_h5yow") +shader_parameter/fill_color = Color(1, 1, 1, 0) +shader_parameter/document_size = Vector2(64, 24) +shader_parameter/document_scale = 1.0 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_c6ww5"] +bg_color = Color(1, 1, 1, 1) +corner_radius_top_left = 4 +corner_radius_top_right = 4 +corner_radius_bottom_right = 4 +corner_radius_bottom_left = 4 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_cax14"] +bg_color = Color(0.6, 0.6, 0.6, 0) + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_y3ecf"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_b5gnl"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_c6wcm"] +bg_color = Color(0, 0, 0, 0.784314) + +[node name="ColorPaintMaterialListItem" type="PanelContainer"] +custom_minimum_size = Vector2(0, 32) +offset_right = 154.0 +offset_bottom = 32.0 +size_flags_horizontal = 3 +mouse_filter = 1 +theme_override_styles/panel = SubResource("StyleBoxFlat_pyity") +script = ExtResource("1_qgde6") +_color_material = SubResource("ShaderMaterial_5rytv") +_linear_gradient_material = SubResource("ShaderMaterial_smcwm") +_radial_gradient_material = SubResource("ShaderMaterial_urnwi") + +[node name="Padding" type="PanelContainer" parent="."] +layout_mode = 2 +mouse_filter = 1 +theme_override_styles/panel = SubResource("StyleBoxFlat_6qubt") + +[node name="HBoxContainer" type="HBoxContainer" parent="Padding"] +layout_mode = 2 +mouse_filter = 2 + +[node name="Button" type="Button" parent="Padding/HBoxContainer"] +custom_minimum_size = Vector2(64, 0) +layout_mode = 2 +focus_mode = 0 +mouse_filter = 1 + +[node name="Color" type="Panel" parent="Padding/HBoxContainer/Button"] +unique_name_in_owner = true +material = SubResource("ShaderMaterial_hsq3x") +custom_minimum_size = Vector2(64, 0) +layout_mode = 2 +offset_right = 64.0 +offset_bottom = 24.0 +size_flags_horizontal = 0 +mouse_filter = 2 +theme_override_styles/panel = SubResource("StyleBoxFlat_c6ww5") + +[node name="NameText" type="LineEdit" parent="Padding/HBoxContainer"] +unique_name_in_owner = true +auto_translate_mode = 2 +layout_mode = 2 +size_flags_horizontal = 3 +mouse_filter = 1 +theme_override_font_sizes/font_size = 12 +theme_override_styles/focus = SubResource("StyleBoxFlat_cax14") +theme_override_styles/read_only = SubResource("StyleBoxEmpty_y3ecf") +theme_override_styles/normal = SubResource("StyleBoxEmpty_b5gnl") +editable = false +context_menu_enabled = false +middle_mouse_paste_enabled = false +selecting_enabled = false +drag_and_drop_selection_enabled = false + +[node name="DraggingtPanel" type="Panel" parent="."] +visible = false +layout_mode = 2 +mouse_filter = 2 +theme_override_styles/panel = SubResource("StyleBoxFlat_c6wcm") + +[node name="PopupPanel" type="PopupPanel" parent="."] +position = Vector2i(0, 44) +size = Vector2i(306, 584) + +[node name="ColorPicker" type="ColorPicker" parent="PopupPanel"] +offset_left = 4.0 +offset_top = 4.0 +offset_right = 302.0 +offset_bottom = 580.0 + +[node name="LinearGradientPicker" parent="." instance=ExtResource("4_iyron")] +visible = false + +[node name="RadialGradientPicker" parent="." instance=ExtResource("6_5vs5c")] +visible = false + +[connection signal="mouse_entered" from="Padding" to="." method="_on_padding_mouse_entered"] +[connection signal="mouse_exited" from="Padding" to="." method="_on_padding_mouse_exited"] +[connection signal="button_up" from="Padding/HBoxContainer/Button" to="." method="_on_button_button_up"] +[connection signal="text_changed" from="Padding/HBoxContainer/NameText" to="." method="_on_line_edit_text_changed"] +[connection signal="text_submitted" from="Padding/HBoxContainer/NameText" to="." method="_on_line_edit_text_submitted"] +[connection signal="popup_hide" from="PopupPanel" to="." method="_on_popup_panel_popup_hide"] +[connection signal="color_changed" from="PopupPanel/ColorPicker" to="." method="_on_color_picker_color_changed"] +[connection signal="on_gradient_changed" from="LinearGradientPicker" to="." method="_on_linear_gradient_picker_on_gradient_changed"] +[connection signal="popup_hide" from="LinearGradientPicker" to="." method="_on_linear_gradient_picker_popup_hide"] +[connection signal="on_gradient_changed" from="RadialGradientPicker" to="." method="_on_radial_gradient_picker_on_gradient_changed"] +[connection signal="popup_hide" from="RadialGradientPicker" to="." method="_on_radial_gradient_picker_popup_hide"] diff --git a/godot/scenes/node/material_select_popup.tscn b/godot/scenes/node/material_select_popup.tscn new file mode 100644 index 0000000..13c5a5c --- /dev/null +++ b/godot/scenes/node/material_select_popup.tscn @@ -0,0 +1,58 @@ +[gd_scene load_steps=6 format=3 uid="uid://bctakxrfgwaor"] + +[ext_resource type="Script" path="res://src/node/material_select_popup.gd" id="1_26x0c"] +[ext_resource type="Shader" path="res://shaders/fill.gdshader" id="2_2bwdp"] +[ext_resource type="Texture2D" uid="uid://bej0ujfdimntj" path="res://icons/font_awesome/magnifying-glass-solid.svg" id="3_3on70"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_rgyto"] +bg_color = Color(0.133333, 0.133333, 0.133333, 1) +border_width_left = 2 +border_width_top = 2 +border_width_right = 2 +border_width_bottom = 2 +border_color = Color(0.529067, 0.529067, 0.529066, 1) + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_fw16f"] +shader = ExtResource("2_2bwdp") +shader_parameter/fill_color = Color(1, 1, 1, 1) + +[node name="MaterialSelectPopup" type="Popup"] +size = Vector2i(240, 320) +visible = true +wrap_controls = false +script = ExtResource("1_26x0c") + +[node name="PanelContainer" type="PanelContainer" parent="."] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_styles/panel = SubResource("StyleBoxFlat_rgyto") + +[node name="VBoxContainer" type="VBoxContainer" parent="PanelContainer"] +layout_mode = 2 + +[node name="LineEdit" type="LineEdit" parent="PanelContainer/VBoxContainer"] +unique_name_in_owner = true +auto_translate_mode = 2 +layout_mode = 2 +placeholder_text = "Search..." + +[node name="Sprite2D" type="Sprite2D" parent="PanelContainer/VBoxContainer/LineEdit"] +material = SubResource("ShaderMaterial_fw16f") +position = Vector2(222, 16) +scale = Vector2(0.04, 0.04) +texture = ExtResource("3_3on70") + +[node name="ScrollContainer" type="ScrollContainer" parent="PanelContainer/VBoxContainer"] +layout_mode = 2 +size_flags_vertical = 3 + +[node name="ListContainer" type="VBoxContainer" parent="PanelContainer/VBoxContainer/ScrollContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 + +[connection signal="visibility_changed" from="." to="." method="_on_visibility_changed"] +[connection signal="text_changed" from="PanelContainer/VBoxContainer/LineEdit" to="." method="_on_line_edit_text_changed"] diff --git a/godot/scenes/node/material_select_popup_item.tscn b/godot/scenes/node/material_select_popup_item.tscn new file mode 100644 index 0000000..591e03a --- /dev/null +++ b/godot/scenes/node/material_select_popup_item.tscn @@ -0,0 +1,90 @@ +[gd_scene load_steps=12 format=3 uid="uid://b20uvm65bbdew"] + +[ext_resource type="Script" path="res://src/node/material_select_popup_item.gd" id="1_13an1"] +[ext_resource type="Shader" path="res://shaders/fill_checker_alpha.gdshader" id="2_nxm34"] +[ext_resource type="Shader" path="res://shaders/linear_gradient_checker_alpha.gdshader" id="3_au23s"] +[ext_resource type="Shader" path="res://shaders/radial_gradient_checker_alpha.gdshader" id="4_3lpqx"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_l0331"] +bg_color = Color(0.6, 0.6, 0.6, 0) + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_pete5"] +shader = ExtResource("2_nxm34") +shader_parameter/fill_color = Color(1, 1, 1, 1) +shader_parameter/document_size = Vector2(480, 720) +shader_parameter/document_scale = 1.0 + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_86o67"] +shader = ExtResource("3_au23s") +shader_parameter/document_size = Vector2(480, 720) +shader_parameter/document_scale = 1.0 + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_y1hs2"] +shader = ExtResource("4_3lpqx") +shader_parameter/document_size = Vector2(480, 720) +shader_parameter/document_scale = 1.0 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ync7d"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 +bg_color = Color(0.6, 0.6, 0.6, 0) + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_erkst"] +shader = ExtResource("2_nxm34") +shader_parameter/fill_color = Color(1, 1, 1, 0) +shader_parameter/document_size = Vector2(64, 24) +shader_parameter/document_scale = 1.0 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_l7so6"] +bg_color = Color(1, 1, 1, 1) +corner_radius_top_left = 4 +corner_radius_top_right = 4 +corner_radius_bottom_right = 4 +corner_radius_bottom_left = 4 + +[node name="MaterialSelectPopupItem" type="PanelContainer"] +anchors_preset = 14 +anchor_top = 0.5 +anchor_right = 1.0 +anchor_bottom = 0.5 +offset_top = -12.5 +offset_bottom = 19.5 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 3 +theme_override_styles/panel = SubResource("StyleBoxFlat_l0331") +script = ExtResource("1_13an1") +_color_material = SubResource("ShaderMaterial_pete5") +_linear_gradient_material = SubResource("ShaderMaterial_86o67") +_radial_gradient_material = SubResource("ShaderMaterial_y1hs2") + +[node name="Padding" type="PanelContainer" parent="."] +layout_mode = 2 +size_flags_horizontal = 3 +mouse_filter = 1 +theme_override_styles/panel = SubResource("StyleBoxFlat_ync7d") + +[node name="HBoxContainer" type="HBoxContainer" parent="Padding"] +layout_mode = 2 +mouse_filter = 2 + +[node name="Color" type="Panel" parent="Padding/HBoxContainer"] +unique_name_in_owner = true +material = SubResource("ShaderMaterial_erkst") +custom_minimum_size = Vector2(64, 0) +layout_mode = 2 +size_flags_horizontal = 0 +mouse_filter = 2 +theme_override_styles/panel = SubResource("StyleBoxFlat_l7so6") + +[node name="Label" type="Label" parent="Padding/HBoxContainer"] +unique_name_in_owner = true +auto_translate_mode = 2 +layout_mode = 2 +size_flags_horizontal = 3 +text = "Material" + +[connection signal="mouse_entered" from="." to="." method="_on_mouse_entered"] +[connection signal="mouse_exited" from="." to="." method="_on_mouse_exited"] diff --git a/godot/scenes/node/path.tscn b/godot/scenes/node/path.tscn new file mode 100644 index 0000000..4a5814d --- /dev/null +++ b/godot/scenes/node/path.tscn @@ -0,0 +1,34 @@ +[gd_scene load_steps=8 format=3 uid="uid://biwyb60wav3xb"] + +[ext_resource type="Script" path="res://src/layer/path.gd" id="1_jh877"] +[ext_resource type="Shader" path="res://shaders/missing_material.gdshader" id="2_dd7m4"] +[ext_resource type="Shader" path="res://shaders/linear_gradient_path.gdshader" id="3_tec1e"] +[ext_resource type="Shader" path="res://shaders/radial_gradient_path.gdshader" id="4_2ehwr"] + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_h2vc1"] +shader = ExtResource("2_dd7m4") + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_yd0m4"] +shader = ExtResource("3_tec1e") +shader_parameter/start_point = Vector2(0, 0) +shader_parameter/end_point = Vector2(1, 0) + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_b7tlp"] +shader = ExtResource("4_2ehwr") +shader_parameter/center_point = Vector2(0, 0) +shader_parameter/handle_1_point = Vector2(1, 0) +shader_parameter/handle_2_point = Vector2(1, 0) + +[node name="Path" type="SubViewport"] +transparent_bg = true +msaa_2d = 1 +canvas_item_default_texture_filter = 2 +render_target_update_mode = 0 +script = ExtResource("1_jh877") +missing_material = SubResource("ShaderMaterial_h2vc1") +linear_gradient_material = SubResource("ShaderMaterial_yd0m4") +radial_gradient_material = SubResource("ShaderMaterial_b7tlp") + +[node name="Line2D" type="Line2D" parent="."] + +[node name="Polygon2D" type="Polygon2D" parent="."] diff --git a/godot/scenes/node/radial_gradient_picker.tscn b/godot/scenes/node/radial_gradient_picker.tscn new file mode 100644 index 0000000..a62ee68 --- /dev/null +++ b/godot/scenes/node/radial_gradient_picker.tscn @@ -0,0 +1,158 @@ +[gd_scene load_steps=3 format=3 uid="uid://bl0lqn8s0d1d3"] + +[ext_resource type="Script" path="res://src/node/radial_gradient_picker.gd" id="1_alx0g"] +[ext_resource type="PackedScene" uid="uid://bdyvforlk3gff" path="res://scenes/node/gradient_editor.tscn" id="2_pfqv6"] + +[node name="RadialGradientPicker" type="Popup"] +size = Vector2i(350, 792) +visible = true +max_size = Vector2i(350, 32768) +script = ExtResource("1_alx0g") + +[node name="PanelContainer" type="PanelContainer" parent="."] +offset_right = 300.0 +offset_bottom = 650.0 + +[node name="VBoxContainer" type="VBoxContainer" parent="PanelContainer"] +layout_mode = 2 + +[node name="VBoxContainer" type="VBoxContainer" parent="PanelContainer/VBoxContainer"] +layout_mode = 2 + +[node name="Padding" type="Control" parent="PanelContainer/VBoxContainer/VBoxContainer"] +layout_mode = 2 + +[node name="HBoxContainer" type="HBoxContainer" parent="PanelContainer/VBoxContainer/VBoxContainer"] +custom_minimum_size = Vector2(350, 0) +layout_mode = 2 + +[node name="Padding" type="Control" parent="PanelContainer/VBoxContainer/VBoxContainer/HBoxContainer"] +custom_minimum_size = Vector2(8, 0) +layout_mode = 2 + +[node name="Label" type="Label" parent="PanelContainer/VBoxContainer/VBoxContainer/HBoxContainer"] +layout_mode = 2 +text = "RADIAL_GRADIENT_CENTER_POINT" + +[node name="HBoxContainer" type="HBoxContainer" parent="PanelContainer/VBoxContainer/VBoxContainer/HBoxContainer"] +layout_mode = 2 + +[node name="Label" type="Label" parent="PanelContainer/VBoxContainer/VBoxContainer/HBoxContainer/HBoxContainer"] +layout_mode = 2 +text = "x: " + +[node name="CenterX" type="SpinBox" parent="PanelContainer/VBoxContainer/VBoxContainer/HBoxContainer/HBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +allow_greater = true +allow_lesser = true + +[node name="Label2" type="Label" parent="PanelContainer/VBoxContainer/VBoxContainer/HBoxContainer/HBoxContainer"] +layout_mode = 2 +text = "y: " + +[node name="CenterY" type="SpinBox" parent="PanelContainer/VBoxContainer/VBoxContainer/HBoxContainer/HBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +allow_greater = true +allow_lesser = true + +[node name="HBoxContainer2" type="HBoxContainer" parent="PanelContainer/VBoxContainer/VBoxContainer"] +layout_mode = 2 + +[node name="Padding" type="Control" parent="PanelContainer/VBoxContainer/VBoxContainer/HBoxContainer2"] +custom_minimum_size = Vector2(8, 0) +layout_mode = 2 + +[node name="Label" type="Label" parent="PanelContainer/VBoxContainer/VBoxContainer/HBoxContainer2"] +layout_mode = 2 +text = "RADIAL_GRADIENT_HANDLE_1" + +[node name="HBoxContainer" type="HBoxContainer" parent="PanelContainer/VBoxContainer/VBoxContainer/HBoxContainer2"] +layout_mode = 2 + +[node name="Label" type="Label" parent="PanelContainer/VBoxContainer/VBoxContainer/HBoxContainer2/HBoxContainer"] +layout_mode = 2 +text = "x: " + +[node name="Handle1X" type="SpinBox" parent="PanelContainer/VBoxContainer/VBoxContainer/HBoxContainer2/HBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +allow_greater = true +allow_lesser = true + +[node name="Label2" type="Label" parent="PanelContainer/VBoxContainer/VBoxContainer/HBoxContainer2/HBoxContainer"] +layout_mode = 2 +text = "y: " + +[node name="Handle1Y" type="SpinBox" parent="PanelContainer/VBoxContainer/VBoxContainer/HBoxContainer2/HBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +allow_greater = true +allow_lesser = true + +[node name="HBoxContainer4" type="HBoxContainer" parent="PanelContainer/VBoxContainer/VBoxContainer"] +layout_mode = 2 + +[node name="Padding" type="Control" parent="PanelContainer/VBoxContainer/VBoxContainer/HBoxContainer4"] +custom_minimum_size = Vector2(8, 0) +layout_mode = 2 + +[node name="Label" type="Label" parent="PanelContainer/VBoxContainer/VBoxContainer/HBoxContainer4"] +layout_mode = 2 +text = "RADIAL_GRADIENT_HANDLE_2" + +[node name="HBoxContainer" type="HBoxContainer" parent="PanelContainer/VBoxContainer/VBoxContainer/HBoxContainer4"] +layout_mode = 2 + +[node name="Label" type="Label" parent="PanelContainer/VBoxContainer/VBoxContainer/HBoxContainer4/HBoxContainer"] +layout_mode = 2 +text = "x: " + +[node name="Handle2X" type="SpinBox" parent="PanelContainer/VBoxContainer/VBoxContainer/HBoxContainer4/HBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +allow_greater = true +allow_lesser = true + +[node name="Label2" type="Label" parent="PanelContainer/VBoxContainer/VBoxContainer/HBoxContainer4/HBoxContainer"] +layout_mode = 2 +text = "y: " + +[node name="Handle2Y" type="SpinBox" parent="PanelContainer/VBoxContainer/VBoxContainer/HBoxContainer4/HBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +allow_greater = true +allow_lesser = true + +[node name="HBoxContainer3" type="HBoxContainer" parent="PanelContainer/VBoxContainer/VBoxContainer"] +layout_mode = 2 + +[node name="Padding" type="Control" parent="PanelContainer/VBoxContainer/VBoxContainer/HBoxContainer3"] +custom_minimum_size = Vector2(8, 0) +layout_mode = 2 + +[node name="EditButton" type="Button" parent="PanelContainer/VBoxContainer/VBoxContainer/HBoxContainer3"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "RADIAL_GRADIENT_EDIT" + +[node name="Padding2" type="Control" parent="PanelContainer/VBoxContainer/VBoxContainer/HBoxContainer3"] +custom_minimum_size = Vector2(8, 0) +layout_mode = 2 + +[node name="Padding2" type="Control" parent="PanelContainer/VBoxContainer/VBoxContainer"] +layout_mode = 2 + +[node name="GradientEditor" parent="PanelContainer/VBoxContainer" instance=ExtResource("2_pfqv6")] +unique_name_in_owner = true +layout_mode = 2 + +[connection signal="popup_hide" from="." to="." method="_on_popup_hide"] +[connection signal="value_changed" from="PanelContainer/VBoxContainer/VBoxContainer/HBoxContainer/HBoxContainer/CenterX" to="." method="_on_center_x_value_changed"] +[connection signal="value_changed" from="PanelContainer/VBoxContainer/VBoxContainer/HBoxContainer/HBoxContainer/CenterY" to="." method="_on_center_y_value_changed"] +[connection signal="value_changed" from="PanelContainer/VBoxContainer/VBoxContainer/HBoxContainer2/HBoxContainer/Handle1X" to="." method="_on_handle_1x_value_changed"] +[connection signal="value_changed" from="PanelContainer/VBoxContainer/VBoxContainer/HBoxContainer2/HBoxContainer/Handle1Y" to="." method="_on_handle_1y_value_changed"] +[connection signal="value_changed" from="PanelContainer/VBoxContainer/VBoxContainer/HBoxContainer4/HBoxContainer/Handle2X" to="." method="_on_handle_2x_value_changed"] +[connection signal="value_changed" from="PanelContainer/VBoxContainer/VBoxContainer/HBoxContainer4/HBoxContainer/Handle2Y" to="." method="_on_handle_2y_value_changed"] +[connection signal="pressed" from="PanelContainer/VBoxContainer/VBoxContainer/HBoxContainer3/EditButton" to="." method="_on_edit_button_pressed"] diff --git a/godot/scenes/sub_window/config_window.tscn b/godot/scenes/sub_window/config_window.tscn new file mode 100644 index 0000000..93b622a --- /dev/null +++ b/godot/scenes/sub_window/config_window.tscn @@ -0,0 +1,87 @@ +[gd_scene load_steps=2 format=3 uid="uid://dd4oq7jxlensx"] + +[ext_resource type="Script" path="res://src/sub_window/config_window.gd" id="1_p462n"] + +[node name="ConfigWindow" type="Window"] +title = "CONFIG_TITLE" +initial_position = 2 +size = Vector2i(420, 160) +transient = true +unresizable = true +popup_window = true +script = ExtResource("1_p462n") + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_constants/separation = 12 +alignment = 1 + +[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 4 +alignment = 1 + +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/VBoxContainer"] +layout_mode = 2 + +[node name="Label" type="Label" parent="VBoxContainer/VBoxContainer/HBoxContainer"] +layout_mode = 2 +text = "CONFIG_LANGUAGE" + +[node name="LanguageMenuButton" type="MenuButton" parent="VBoxContainer/VBoxContainer/HBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +text = "CONFIG_CURRENT_LANGUAGE" +flat = false +item_count = 2 +popup/item_0/text = "日本語" +popup/item_1/text = "English" +popup/item_1/id = 1 + +[node name="HBoxContainer3" type="HBoxContainer" parent="VBoxContainer/VBoxContainer"] +layout_mode = 2 + +[node name="Label" type="Label" parent="VBoxContainer/VBoxContainer/HBoxContainer3"] +layout_mode = 2 +text = "CONFIG_ENABLE_AUTO_SAVE" + +[node name="AutoSaveCheckBox" type="CheckBox" parent="VBoxContainer/VBoxContainer/HBoxContainer3"] +unique_name_in_owner = true +layout_mode = 2 + +[node name="HBoxContainer4" type="HBoxContainer" parent="VBoxContainer/VBoxContainer"] +layout_mode = 2 + +[node name="Label" type="Label" parent="VBoxContainer/VBoxContainer/HBoxContainer4"] +layout_mode = 2 +text = "CONFIG_AUTO_SAVE_INTERVAL" + +[node name="AutoSaveSpinBox" type="SpinBox" parent="VBoxContainer/VBoxContainer/HBoxContainer4"] +unique_name_in_owner = true +layout_mode = 2 +min_value = 1.0 +max_value = 3600.0 +value = 300.0 + +[node name="HBoxContainer2" type="HBoxContainer" parent="VBoxContainer"] +layout_mode = 2 +alignment = 2 + +[node name="CloseButton" type="Button" parent="VBoxContainer/HBoxContainer2"] +layout_mode = 2 +text = "CONFIG_CLOSE" + +[node name="Space" type="Control" parent="VBoxContainer/HBoxContainer2"] +custom_minimum_size = Vector2(24, 0) +layout_mode = 2 + +[connection signal="close_requested" from="." to="." method="_on_close_requested"] +[connection signal="toggled" from="VBoxContainer/VBoxContainer/HBoxContainer3/AutoSaveCheckBox" to="." method="_on_auto_save_check_box_toggled"] +[connection signal="value_changed" from="VBoxContainer/VBoxContainer/HBoxContainer4/AutoSaveSpinBox" to="." method="_on_auto_save_spin_box_value_changed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer2/CloseButton" to="." method="_on_close_button_pressed"] diff --git a/godot/scenes/sub_window/document_export_window.tscn b/godot/scenes/sub_window/document_export_window.tscn new file mode 100644 index 0000000..1890da1 --- /dev/null +++ b/godot/scenes/sub_window/document_export_window.tscn @@ -0,0 +1,86 @@ +[gd_scene load_steps=2 format=3 uid="uid://dferwtnot6vxw"] + +[ext_resource type="Script" path="res://src/sub_window/document_export_window.gd" id="1_tdgkl"] + +[node name="DocumentExportWindow" type="Window"] +title = "EXPORT_TITLE" +initial_position = 2 +size = Vector2i(420, 100) +transient = true +unresizable = true +popup_window = true +script = ExtResource("1_tdgkl") + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_constants/separation = 12 +alignment = 1 + +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer"] +layout_mode = 2 +alignment = 1 + +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/HBoxContainer"] +layout_mode = 2 + +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/HBoxContainer/HBoxContainer"] +layout_mode = 2 + +[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer/HBoxContainer/HBoxContainer"] +layout_mode = 2 +text = "EXPORT_WIDTH" + +[node name="WidthSpinBox" type="SpinBox" parent="VBoxContainer/HBoxContainer/HBoxContainer/HBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +min_value = 1.0 +max_value = 2048.0 +value = 1.0 +select_all_on_focus = true + +[node name="HBoxContainer2" type="HBoxContainer" parent="VBoxContainer/HBoxContainer/HBoxContainer"] +layout_mode = 2 + +[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer/HBoxContainer/HBoxContainer2"] +layout_mode = 2 +text = "EXPORT_HEIGHT" + +[node name="HeightSpinBox" type="SpinBox" parent="VBoxContainer/HBoxContainer/HBoxContainer/HBoxContainer2"] +unique_name_in_owner = true +layout_mode = 2 +min_value = 1.0 +max_value = 2048.0 +value = 1.0 +select_all_on_focus = true + +[node name="HBoxContainer2" type="HBoxContainer" parent="VBoxContainer"] +layout_mode = 2 +alignment = 2 + +[node name="ExportButton" type="Button" parent="VBoxContainer/HBoxContainer2"] +layout_mode = 2 +text = "EXPORT_EXPORT" + +[node name="Space2" type="Control" parent="VBoxContainer/HBoxContainer2"] +custom_minimum_size = Vector2(4, 0) +layout_mode = 2 + +[node name="CancelButton" type="Button" parent="VBoxContainer/HBoxContainer2"] +layout_mode = 2 +text = "EXPORT_CANCEL" + +[node name="Space" type="Control" parent="VBoxContainer/HBoxContainer2"] +custom_minimum_size = Vector2(24, 0) +layout_mode = 2 + +[connection signal="close_requested" from="." to="." method="_on_close_requested"] +[connection signal="value_changed" from="VBoxContainer/HBoxContainer/HBoxContainer/HBoxContainer/WidthSpinBox" to="." method="_on_width_spin_box_value_changed"] +[connection signal="value_changed" from="VBoxContainer/HBoxContainer/HBoxContainer/HBoxContainer2/HeightSpinBox" to="." method="_on_height_spin_box_value_changed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer2/ExportButton" to="." method="_on_create_button_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer2/CancelButton" to="." method="_on_cancel_button_pressed"] diff --git a/godot/scenes/sub_window/document_open_window.tscn b/godot/scenes/sub_window/document_open_window.tscn new file mode 100644 index 0000000..48a9028 --- /dev/null +++ b/godot/scenes/sub_window/document_open_window.tscn @@ -0,0 +1,125 @@ +[gd_scene load_steps=5 format=3 uid="uid://b0t6n35a5uvxu"] + +[ext_resource type="Script" path="res://src/sub_window/document_open_window.gd" id="1_n32n5"] +[ext_resource type="Shader" path="res://shaders/fill.gdshader" id="2_qtysa"] +[ext_resource type="Texture2D" uid="uid://cnqkj40h44qeh" path="res://icons/font_awesome/arrow-right-arrow-left-solid.svg" id="3_rub81"] + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_sfrdi"] +shader = ExtResource("2_qtysa") +shader_parameter/fill_color = Color(1, 1, 1, 1) + +[node name="DocumentOpenWindow" type="Window"] +title = "NEW_DOCUMENT_TITLE" +initial_position = 2 +size = Vector2i(420, 160) +transient = true +unresizable = true +popup_window = true +script = ExtResource("1_n32n5") + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_constants/separation = 12 +alignment = 1 + +[node name="MenuButton" type="MenuButton" parent="VBoxContainer"] +unique_name_in_owner = true +custom_minimum_size = Vector2(240, 0) +layout_mode = 2 +size_flags_horizontal = 4 +text = "NEW_DOCUMENT_SIZE_PRESET" +flat = false +item_count = 8 +popup/item_0/text = "800x600" +popup/item_1/text = "1024x600" +popup/item_1/id = 1 +popup/item_2/text = "1024x768" +popup/item_2/id = 2 +popup/item_3/text = "1280x720" +popup/item_3/id = 3 +popup/item_4/text = "1280x960" +popup/item_4/id = 4 +popup/item_5/text = "1600x900" +popup/item_5/id = 5 +popup/item_6/text = "1600x1200" +popup/item_6/id = 6 +popup/item_7/text = "1920x1080" +popup/item_7/id = 7 + +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer"] +layout_mode = 2 +alignment = 1 + +[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/HBoxContainer"] +layout_mode = 2 + +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/HBoxContainer/VBoxContainer"] +layout_mode = 2 + +[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer/VBoxContainer/HBoxContainer"] +layout_mode = 2 +text = "NEW_DOCUMENT_WIDTH" + +[node name="WidthSpinBox" type="SpinBox" parent="VBoxContainer/HBoxContainer/VBoxContainer/HBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +min_value = 1.0 +max_value = 2048.0 +value = 1.0 +select_all_on_focus = true + +[node name="HBoxContainer2" type="HBoxContainer" parent="VBoxContainer/HBoxContainer/VBoxContainer"] +layout_mode = 2 + +[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer/VBoxContainer/HBoxContainer2"] +layout_mode = 2 +text = "NEW_DOCUMENT_HEIGHT" + +[node name="HeightSpinBox" type="SpinBox" parent="VBoxContainer/HBoxContainer/VBoxContainer/HBoxContainer2"] +unique_name_in_owner = true +layout_mode = 2 +min_value = 1.0 +max_value = 2048.0 +value = 1.0 +select_all_on_focus = true + +[node name="SwitchButton" type="Button" parent="VBoxContainer/HBoxContainer"] +custom_minimum_size = Vector2(24, 0) +layout_mode = 2 + +[node name="Sprite2D" type="Sprite2D" parent="VBoxContainer/HBoxContainer/SwitchButton"] +material = SubResource("ShaderMaterial_sfrdi") +position = Vector2(12, 32) +scale = Vector2(0.03, 0.03) +texture = ExtResource("3_rub81") + +[node name="HBoxContainer2" type="HBoxContainer" parent="VBoxContainer"] +layout_mode = 2 +alignment = 2 + +[node name="CreateButton" type="Button" parent="VBoxContainer/HBoxContainer2"] +layout_mode = 2 +text = "NEW_DOCUMENT_CREATE" + +[node name="Space2" type="Control" parent="VBoxContainer/HBoxContainer2"] +custom_minimum_size = Vector2(4, 0) +layout_mode = 2 + +[node name="CancelButton" type="Button" parent="VBoxContainer/HBoxContainer2"] +layout_mode = 2 +text = "NEW_DOCUMENT_CANCEL" + +[node name="Space" type="Control" parent="VBoxContainer/HBoxContainer2"] +custom_minimum_size = Vector2(24, 0) +layout_mode = 2 + +[connection signal="close_requested" from="." to="." method="_on_close_requested"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/SwitchButton" to="." method="_on_switch_button_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer2/CreateButton" to="." method="_on_create_button_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer2/CancelButton" to="." method="_on_cancel_button_pressed"] diff --git a/godot/scenes/sub_window/document_size_change_window.tscn b/godot/scenes/sub_window/document_size_change_window.tscn new file mode 100644 index 0000000..22bc9b5 --- /dev/null +++ b/godot/scenes/sub_window/document_size_change_window.tscn @@ -0,0 +1,319 @@ +[gd_scene load_steps=6 format=3 uid="uid://7rdbvmswbjw3"] + +[ext_resource type="Script" path="res://src/sub_window/document_size_change_window.gd" id="1_bar4x"] +[ext_resource type="Shader" path="res://shaders/fill.gdshader" id="2_tk6pd"] +[ext_resource type="Texture2D" uid="uid://cnqkj40h44qeh" path="res://icons/font_awesome/arrow-right-arrow-left-solid.svg" id="3_23iw0"] + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_sfrdi"] +shader = ExtResource("2_tk6pd") +shader_parameter/fill_color = Color(1, 1, 1, 1) + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_6sreq"] + +[node name="DocumentSizeChangeWindow" type="Window"] +title = "DOCUMENT_SIZE_TITLE" +initial_position = 2 +size = Vector2i(420, 200) +transient = true +unresizable = true +popup_window = true +script = ExtResource("1_bar4x") + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_constants/separation = 12 +alignment = 1 + +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer"] +layout_mode = 2 +alignment = 1 + +[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/HBoxContainer"] +layout_mode = 2 + +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/HBoxContainer/VBoxContainer"] +layout_mode = 2 + +[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer/VBoxContainer/HBoxContainer"] +layout_mode = 2 +text = "DOCUMENT_SIZE_WIDTH" + +[node name="WidthSpinBox" type="SpinBox" parent="VBoxContainer/HBoxContainer/VBoxContainer/HBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +min_value = 1.0 +max_value = 2048.0 +value = 1.0 +select_all_on_focus = true + +[node name="HBoxContainer2" type="HBoxContainer" parent="VBoxContainer/HBoxContainer/VBoxContainer"] +layout_mode = 2 + +[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer/VBoxContainer/HBoxContainer2"] +layout_mode = 2 +text = "DOCUMENT_SIZE_HEIGHT" + +[node name="HeightSpinBox" type="SpinBox" parent="VBoxContainer/HBoxContainer/VBoxContainer/HBoxContainer2"] +unique_name_in_owner = true +layout_mode = 2 +min_value = 1.0 +max_value = 2048.0 +value = 1.0 +select_all_on_focus = true + +[node name="SwitchButton" type="Button" parent="VBoxContainer/HBoxContainer"] +custom_minimum_size = Vector2(24, 0) +layout_mode = 2 + +[node name="Sprite2D" type="Sprite2D" parent="VBoxContainer/HBoxContainer/SwitchButton"] +material = SubResource("ShaderMaterial_sfrdi") +position = Vector2(12, 32) +scale = Vector2(0.03, 0.03) +texture = ExtResource("3_23iw0") + +[node name="HBoxConrtainer" type="HBoxContainer" parent="VBoxContainer"] +custom_minimum_size = Vector2(64, 64) +layout_mode = 2 +alignment = 1 + +[node name="Label" type="Label" parent="VBoxContainer/HBoxConrtainer"] +layout_mode = 2 +size_flags_vertical = 1 +text = "DOCUMENT_SIZE_ANCHOR" + +[node name="Panel" type="Panel" parent="VBoxContainer/HBoxConrtainer"] +custom_minimum_size = Vector2(90, 0) +layout_mode = 2 +theme_override_styles/panel = SubResource("StyleBoxEmpty_6sreq") + +[node name="Panel" type="Panel" parent="VBoxContainer/HBoxConrtainer/Panel"] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -20.0 +offset_top = -20.0 +offset_right = 20.0 +offset_bottom = 20.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="TopLeftButton" type="Button" parent="VBoxContainer/HBoxConrtainer/Panel/Panel"] +unique_name_in_owner = true +layout_mode = 0 +offset_left = -6.0 +offset_top = -6.0 +offset_right = 6.0 +offset_bottom = 6.0 + +[node name="ColorRect" type="ColorRect" parent="VBoxContainer/HBoxConrtainer/Panel/Panel/TopLeftButton"] +layout_mode = 0 +offset_right = 12.0 +offset_bottom = 12.0 +mouse_filter = 2 +color = Color(0.5, 0.5, 0.5, 1) + +[node name="TopButton" type="Button" parent="VBoxContainer/HBoxConrtainer/Panel/Panel"] +unique_name_in_owner = true +layout_mode = 1 +anchors_preset = 5 +anchor_left = 0.5 +anchor_right = 0.5 +offset_left = -6.0 +offset_top = -6.0 +offset_right = 6.0 +offset_bottom = 6.0 +grow_horizontal = 2 + +[node name="ColorRect" type="ColorRect" parent="VBoxContainer/HBoxConrtainer/Panel/Panel/TopButton"] +layout_mode = 0 +offset_right = 12.0 +offset_bottom = 12.0 +mouse_filter = 2 +color = Color(0.5, 0.5, 0.5, 1) + +[node name="TopRightButton" type="Button" parent="VBoxContainer/HBoxConrtainer/Panel/Panel"] +unique_name_in_owner = true +layout_mode = 1 +anchors_preset = 1 +anchor_left = 1.0 +anchor_right = 1.0 +offset_left = -6.0 +offset_top = -6.0 +offset_right = 6.0 +offset_bottom = 6.0 +grow_horizontal = 0 + +[node name="ColorRect" type="ColorRect" parent="VBoxContainer/HBoxConrtainer/Panel/Panel/TopRightButton"] +layout_mode = 0 +offset_right = 12.0 +offset_bottom = 12.0 +mouse_filter = 2 +color = Color(0.5, 0.5, 0.5, 1) + +[node name="LeftButton" type="Button" parent="VBoxContainer/HBoxConrtainer/Panel/Panel"] +unique_name_in_owner = true +layout_mode = 1 +anchors_preset = 4 +anchor_top = 0.5 +anchor_bottom = 0.5 +offset_left = -6.0 +offset_top = -6.0 +offset_right = 6.0 +offset_bottom = 6.0 +grow_vertical = 2 + +[node name="ColorRect" type="ColorRect" parent="VBoxContainer/HBoxConrtainer/Panel/Panel/LeftButton"] +layout_mode = 0 +offset_right = 12.0 +offset_bottom = 12.0 +mouse_filter = 2 +color = Color(0.5, 0.5, 0.5, 1) + +[node name="CenterButton" type="Button" parent="VBoxContainer/HBoxConrtainer/Panel/Panel"] +unique_name_in_owner = true +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -6.0 +offset_top = -6.0 +offset_right = 6.0 +offset_bottom = 6.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="ColorRect" type="ColorRect" parent="VBoxContainer/HBoxConrtainer/Panel/Panel/CenterButton"] +layout_mode = 0 +offset_right = 12.0 +offset_bottom = 12.0 +mouse_filter = 2 + +[node name="RightButton" type="Button" parent="VBoxContainer/HBoxConrtainer/Panel/Panel"] +unique_name_in_owner = true +layout_mode = 1 +anchors_preset = 6 +anchor_left = 1.0 +anchor_top = 0.5 +anchor_right = 1.0 +anchor_bottom = 0.5 +offset_left = -6.0 +offset_top = -6.0 +offset_right = 6.0 +offset_bottom = 6.0 +grow_horizontal = 0 +grow_vertical = 2 + +[node name="ColorRect" type="ColorRect" parent="VBoxContainer/HBoxConrtainer/Panel/Panel/RightButton"] +layout_mode = 0 +offset_right = 12.0 +offset_bottom = 12.0 +mouse_filter = 2 +color = Color(0.5, 0.5, 0.5, 1) + +[node name="BottomLeftButton" type="Button" parent="VBoxContainer/HBoxConrtainer/Panel/Panel"] +unique_name_in_owner = true +layout_mode = 1 +anchors_preset = 2 +anchor_top = 1.0 +anchor_bottom = 1.0 +offset_left = -6.0 +offset_top = -6.0 +offset_right = 6.0 +offset_bottom = 6.0 +grow_vertical = 0 + +[node name="ColorRect" type="ColorRect" parent="VBoxContainer/HBoxConrtainer/Panel/Panel/BottomLeftButton"] +layout_mode = 0 +offset_right = 12.0 +offset_bottom = 12.0 +mouse_filter = 2 +color = Color(0.5, 0.5, 0.5, 1) + +[node name="BottomButton" type="Button" parent="VBoxContainer/HBoxConrtainer/Panel/Panel"] +unique_name_in_owner = true +layout_mode = 1 +anchors_preset = 7 +anchor_left = 0.5 +anchor_top = 1.0 +anchor_right = 0.5 +anchor_bottom = 1.0 +offset_left = -6.0 +offset_top = -6.0 +offset_right = 6.0 +offset_bottom = 6.0 +grow_horizontal = 2 +grow_vertical = 0 + +[node name="ColorRect" type="ColorRect" parent="VBoxContainer/HBoxConrtainer/Panel/Panel/BottomButton"] +layout_mode = 0 +offset_right = 12.0 +offset_bottom = 12.0 +mouse_filter = 2 +color = Color(0.5, 0.5, 0.5, 1) + +[node name="BottomRightButton" type="Button" parent="VBoxContainer/HBoxConrtainer/Panel/Panel"] +unique_name_in_owner = true +layout_mode = 1 +anchors_preset = 3 +anchor_left = 1.0 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = -6.0 +offset_top = -6.0 +offset_right = 6.0 +offset_bottom = 6.0 +grow_horizontal = 0 +grow_vertical = 0 + +[node name="ColorRect" type="ColorRect" parent="VBoxContainer/HBoxConrtainer/Panel/Panel/BottomRightButton"] +layout_mode = 0 +offset_right = 12.0 +offset_bottom = 12.0 +mouse_filter = 2 +color = Color(0.5, 0.5, 0.5, 1) + +[node name="HBoxContainer2" type="HBoxContainer" parent="VBoxContainer"] +layout_mode = 2 +alignment = 2 + +[node name="CreateButton" type="Button" parent="VBoxContainer/HBoxContainer2"] +layout_mode = 2 +text = "DOCUMENT_SIZE_CHANGE_SIZE" + +[node name="Space2" type="Control" parent="VBoxContainer/HBoxContainer2"] +custom_minimum_size = Vector2(4, 0) +layout_mode = 2 + +[node name="CancelButton" type="Button" parent="VBoxContainer/HBoxContainer2"] +layout_mode = 2 +text = "DOCUMENT_SIZE_CANCEL" + +[node name="Space" type="Control" parent="VBoxContainer/HBoxContainer2"] +custom_minimum_size = Vector2(24, 0) +layout_mode = 2 + +[connection signal="close_requested" from="." to="." method="_on_close_requested"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/SwitchButton" to="." method="_on_switch_button_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxConrtainer/Panel/Panel/TopLeftButton" to="." method="_on_top_left_button_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxConrtainer/Panel/Panel/TopButton" to="." method="_on_top_button_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxConrtainer/Panel/Panel/TopRightButton" to="." method="_on_top_right_button_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxConrtainer/Panel/Panel/LeftButton" to="." method="_on_left_button_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxConrtainer/Panel/Panel/CenterButton" to="." method="_on_center_button_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxConrtainer/Panel/Panel/RightButton" to="." method="_on_right_button_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxConrtainer/Panel/Panel/BottomLeftButton" to="." method="_on_bottom_left_button_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxConrtainer/Panel/Panel/BottomButton" to="." method="_on_bottom_button_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxConrtainer/Panel/Panel/BottomRightButton" to="." method="_on_bottom_right_button_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer2/CreateButton" to="." method="_on_create_button_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer2/CancelButton" to="." method="_on_cancel_button_pressed"] diff --git a/godot/scenes/sub_window/licenses_window.tscn b/godot/scenes/sub_window/licenses_window.tscn new file mode 100644 index 0000000..e0dec96 --- /dev/null +++ b/godot/scenes/sub_window/licenses_window.tscn @@ -0,0 +1,4081 @@ +[gd_scene load_steps=4 format=3 uid="uid://b85jraf3yfbfl"] + +[ext_resource type="Script" path="res://src/sub_window/licenses_window.gd" id="1_em3jw"] +[ext_resource type="FontFile" uid="uid://detoutj1j8iin" path="res://RobotoMono-SemiBold.ttf" id="2_1yt3u"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_st6ro"] + +[node name="LicensesWindow" type="Window"] +title = "LICENSE" +initial_position = 2 +size = Vector2i(840, 420) +script = ExtResource("1_em3jw") + +[node name="PanelContainer" type="PanelContainer" parent="."] +offset_right = 840.0 +offset_bottom = 420.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="VBoxContainer" type="VBoxContainer" parent="PanelContainer"] +layout_mode = 2 + +[node name="ScrollContainer" type="ScrollContainer" parent="PanelContainer/VBoxContainer"] +layout_mode = 2 +size_flags_vertical = 3 + +[node name="Panel" type="PanelContainer" parent="PanelContainer/VBoxContainer/ScrollContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_styles/panel = SubResource("StyleBoxEmpty_st6ro") + +[node name="RichTextLabel" type="RichTextLabel" parent="PanelContainer/VBoxContainer/ScrollContainer/Panel"] +auto_translate_mode = 2 +layout_mode = 2 +theme_override_fonts/normal_font = ExtResource("2_1yt3u") +bbcode_enabled = true +text = "[center][b]Godot[/b][/center] + +Copyright (c) 2014-present Godot Engine contributors (see [url=\"https://github.com/godotengine/godot/blob/master/AUTHORS.md\"]AUTHORS.md[/url]). +Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the \"Software\"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]godot-dockable-container[/b][/center] + +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN \"AS-IS\" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an \"owner\") of an original work of +authorship and/or a database (each, a \"Work\"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works (\"Commons\") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the \"Affirmer\"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights (\"Copyright and +Related Rights\"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the \"Waiver\"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +\"License\"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. + +[center]=== === ===[/center] + + +[center][b]godot-uuid[/b][/center] + +MIT License + +Copyright (c) 2023 Xavier Sellier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the \"Software\"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]unicode-ident[/b][/center] + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the \"Software\"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]unicode-ident[/b][/center] + +UNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWARE + +See Terms of Use +for definitions of Unicode Inc.’s Data Files and Software. + +NOTICE TO USER: Carefully read the following legal agreement. +BY DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING UNICODE INC.'S +DATA FILES (\"DATA FILES\"), AND/OR SOFTWARE (\"SOFTWARE\"), +YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE +TERMS AND CONDITIONS OF THIS AGREEMENT. +IF YOU DO NOT AGREE, DO NOT DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE +THE DATA FILES OR SOFTWARE. + +COPYRIGHT AND PERMISSION NOTICE + +Copyright © 1991-2022 Unicode, Inc. All rights reserved. +Distributed under the Terms of Use in https://www.unicode.org/copyright.html. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Unicode data files and any associated documentation +(the \"Data Files\") or Unicode software and any associated documentation +(the \"Software\") to deal in the Data Files or Software +without restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, and/or sell copies of +the Data Files or Software, and to permit persons to whom the Data Files +or Software are furnished to do so, provided that either +(a) this copyright and permission notice appear with all copies +of the Data Files or Software, or +(b) this copyright and permission notice appear in associated +Documentation. + +THE DATA FILES AND SOFTWARE ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT OF THIRD PARTY RIGHTS. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS +NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL +DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, +DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THE DATA FILES OR SOFTWARE. + +Except as contained in this notice, the name of a copyright holder +shall not be used in advertising or otherwise to promote the sale, +use or other dealings in these Data Files or Software without prior +written authorization of the copyright holder. + +[center]=== === ===[/center] + + + +[center][b]wasi[/b][/center] + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the \"Software\"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]cfg-if[/b][/center] + +Copyright (c) 2014 Alex Crichton + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the \"Software\"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]gensym[/b][/center] + +Copyright (c) 2015 The cargo-readme Developers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the \"Software\"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]getrandom[/b][/center] + +Copyright (c) 2018-2024 The rust-random Project Developers +Copyright (c) 2014 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the \"Software\"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]glam[/b][/center] + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the \"Software\"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]heck[/b][/center] + +Copyright (c) 2015 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the \"Software\"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]libc[/b][/center] + +Copyright (c) 2014-2020 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the \"Software\"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]nanoserde[/b][/center] + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +[center]=== === ===[/center] + + +[center][b]paste[/b][/center] + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the \"Software\"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]proc-macro2[/b][/center] + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the \"Software\"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]quote[/b][/center] + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the \"Software\"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]regex[/b][/center] + +Copyright (c) 2014 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the \"Software\"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]regex-automata[/b][/center] + +Copyright (c) 2014 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the \"Software\"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]regex-syntax[/b][/center] + +Copyright (c) 2014 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the \"Software\"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]serde[/b][/center] + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the \"Software\"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]serde_derive[/b][/center] + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the \"Software\"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]syn[/b][/center] + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the \"Software\"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]uuid[/b][/center] + +Copyright (c) 2014 The Rust Project Developers +Copyright (c) 2018 Ashley Mannix, Christopher Armstrong, Dylan DPC, Hunar Roop Kahlon + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the \"Software\"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]i_float[/b][/center] + +MIT License + +Copyright (c) 2023 iShape-Rust + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the \"Software\"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]i_overlay[/b][/center] + +MIT License + +Copyright (c) 2023 iShape-Rust + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the \"Software\"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]i_shape[/b][/center] + +MIT License + +Copyright (c) 2023 iShape-Rust + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the \"Software\"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]i_tree[/b][/center] + +MIT License + +Copyright (c) 2024 iShape-Rust + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the \"Software\"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]i_triangle[/b][/center] + +MIT License + +Copyright (c) 2023 iShape-Rust + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the \"Software\"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]nanoserde-derive[/b][/center] + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]venial[/b][/center] + +MIT License + +Copyright (c) 2022 Olivier FAURE + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the \"Software\"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]aho-corasick[/b][/center] + +The MIT License (MIT) + +Copyright (c) 2015 Andrew Gallant + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the \"Software\"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]memchr[/b][/center] + +The MIT License (MIT) + +Copyright (c) 2015 Andrew Gallant + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the \"Software\"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +[center]=== === ===[/center] + + +[center][b]gdextension-api[/b][/center] + +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. \"Contributor\" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. \"Contributor Version\" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. \"Contribution\" + means Covered Software of a particular Contributor. + +1.4. \"Covered Software\" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. \"Incompatible With Secondary Licenses\" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. \"Executable Form\" + means any form of the work other than Source Code Form. + +1.7. \"Larger Work\" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. \"License\" + means this document. + +1.9. \"Licensable\" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. \"Modifications\" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. \"Patent Claims\" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. \"Secondary License\" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. \"Source Code Form\" + means the form of the work preferred for making modifications. + +1.14. \"You\" (or \"Your\") + means an individual or a legal entity exercising rights under this + License. For legal entities, \"You\" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, \"control\" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an \"as is\" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at https://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - \"Incompatible With Secondary Licenses\" Notice +--------------------------------------------------------- + + This Source Code Form is \"Incompatible With Secondary Licenses\", as + defined by the Mozilla Public License, v. 2.0. + +[center]=== === ===[/center] + + +[center][b]godot[/b][/center] + +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. \"Contributor\" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. \"Contributor Version\" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. \"Contribution\" + means Covered Software of a particular Contributor. + +1.4. \"Covered Software\" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. \"Incompatible With Secondary Licenses\" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. \"Executable Form\" + means any form of the work other than Source Code Form. + +1.7. \"Larger Work\" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. \"License\" + means this document. + +1.9. \"Licensable\" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. \"Modifications\" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. \"Patent Claims\" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. \"Secondary License\" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. \"Source Code Form\" + means the form of the work preferred for making modifications. + +1.14. \"You\" (or \"Your\") + means an individual or a legal entity exercising rights under this + License. For legal entities, \"You\" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, \"control\" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an \"as is\" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at https://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - \"Incompatible With Secondary Licenses\" Notice +--------------------------------------------------------- + + This Source Code Form is \"Incompatible With Secondary Licenses\", as + defined by the Mozilla Public License, v. 2.0. + +[center]=== === ===[/center] + + +[center][b]godot-bindings[/b][/center] + +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. \"Contributor\" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. \"Contributor Version\" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. \"Contribution\" + means Covered Software of a particular Contributor. + +1.4. \"Covered Software\" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. \"Incompatible With Secondary Licenses\" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. \"Executable Form\" + means any form of the work other than Source Code Form. + +1.7. \"Larger Work\" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. \"License\" + means this document. + +1.9. \"Licensable\" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. \"Modifications\" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. \"Patent Claims\" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. \"Secondary License\" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. \"Source Code Form\" + means the form of the work preferred for making modifications. + +1.14. \"You\" (or \"Your\") + means an individual or a legal entity exercising rights under this + License. For legal entities, \"You\" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, \"control\" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an \"as is\" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at https://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - \"Incompatible With Secondary Licenses\" Notice +--------------------------------------------------------- + + This Source Code Form is \"Incompatible With Secondary Licenses\", as + defined by the Mozilla Public License, v. 2.0. + +[center]=== === ===[/center] + + +[center][b]godot-cell[/b][/center] + +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. \"Contributor\" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. \"Contributor Version\" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. \"Contribution\" + means Covered Software of a particular Contributor. + +1.4. \"Covered Software\" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. \"Incompatible With Secondary Licenses\" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. \"Executable Form\" + means any form of the work other than Source Code Form. + +1.7. \"Larger Work\" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. \"License\" + means this document. + +1.9. \"Licensable\" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. \"Modifications\" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. \"Patent Claims\" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. \"Secondary License\" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. \"Source Code Form\" + means the form of the work preferred for making modifications. + +1.14. \"You\" (or \"Your\") + means an individual or a legal entity exercising rights under this + License. For legal entities, \"You\" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, \"control\" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an \"as is\" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at https://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - \"Incompatible With Secondary Licenses\" Notice +--------------------------------------------------------- + + This Source Code Form is \"Incompatible With Secondary Licenses\", as + defined by the Mozilla Public License, v. 2.0. + +[center]=== === ===[/center] + + +[center][b]godot-codegen[/b][/center] + +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. \"Contributor\" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. \"Contributor Version\" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. \"Contribution\" + means Covered Software of a particular Contributor. + +1.4. \"Covered Software\" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. \"Incompatible With Secondary Licenses\" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. \"Executable Form\" + means any form of the work other than Source Code Form. + +1.7. \"Larger Work\" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. \"License\" + means this document. + +1.9. \"Licensable\" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. \"Modifications\" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. \"Patent Claims\" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. \"Secondary License\" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. \"Source Code Form\" + means the form of the work preferred for making modifications. + +1.14. \"You\" (or \"Your\") + means an individual or a legal entity exercising rights under this + License. For legal entities, \"You\" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, \"control\" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an \"as is\" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at https://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - \"Incompatible With Secondary Licenses\" Notice +--------------------------------------------------------- + + This Source Code Form is \"Incompatible With Secondary Licenses\", as + defined by the Mozilla Public License, v. 2.0. + +[center]=== === ===[/center] + + +[center][b]godot-core[/b][/center] + +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. \"Contributor\" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. \"Contributor Version\" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. \"Contribution\" + means Covered Software of a particular Contributor. + +1.4. \"Covered Software\" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. \"Incompatible With Secondary Licenses\" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. \"Executable Form\" + means any form of the work other than Source Code Form. + +1.7. \"Larger Work\" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. \"License\" + means this document. + +1.9. \"Licensable\" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. \"Modifications\" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. \"Patent Claims\" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. \"Secondary License\" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. \"Source Code Form\" + means the form of the work preferred for making modifications. + +1.14. \"You\" (or \"Your\") + means an individual or a legal entity exercising rights under this + License. For legal entities, \"You\" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, \"control\" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an \"as is\" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at https://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - \"Incompatible With Secondary Licenses\" Notice +--------------------------------------------------------- + + This Source Code Form is \"Incompatible With Secondary Licenses\", as + defined by the Mozilla Public License, v. 2.0. + +[center]=== === ===[/center] + + +[center][b]godot-ffi[/b][/center] + +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. \"Contributor\" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. \"Contributor Version\" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. \"Contribution\" + means Covered Software of a particular Contributor. + +1.4. \"Covered Software\" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. \"Incompatible With Secondary Licenses\" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. \"Executable Form\" + means any form of the work other than Source Code Form. + +1.7. \"Larger Work\" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. \"License\" + means this document. + +1.9. \"Licensable\" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. \"Modifications\" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. \"Patent Claims\" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. \"Secondary License\" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. \"Source Code Form\" + means the form of the work preferred for making modifications. + +1.14. \"You\" (or \"Your\") + means an individual or a legal entity exercising rights under this + License. For legal entities, \"You\" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, \"control\" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an \"as is\" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at https://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - \"Incompatible With Secondary Licenses\" Notice +--------------------------------------------------------- + + This Source Code Form is \"Incompatible With Secondary Licenses\", as + defined by the Mozilla Public License, v. 2.0. + +[center]=== === ===[/center] + + +[center][b]godot-macros[/b][/center] + +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. \"Contributor\" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. \"Contributor Version\" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. \"Contribution\" + means Covered Software of a particular Contributor. + +1.4. \"Covered Software\" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. \"Incompatible With Secondary Licenses\" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. \"Executable Form\" + means any form of the work other than Source Code Form. + +1.7. \"Larger Work\" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. \"License\" + means this document. + +1.9. \"Licensable\" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. \"Modifications\" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. \"Patent Claims\" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. \"Secondary License\" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. \"Source Code Form\" + means the form of the work preferred for making modifications. + +1.14. \"You\" (or \"Your\") + means an individual or a legal entity exercising rights under this + License. For legal entities, \"You\" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, \"control\" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an \"as is\" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at https://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - \"Incompatible With Secondary Licenses\" Notice +--------------------------------------------------------- + + This Source Code Form is \"Incompatible With Secondary Licenses\", as + defined by the Mozilla Public License, v. 2.0. +" + +[node name="Button" type="Button" parent="PanelContainer/VBoxContainer"] +layout_mode = 2 +text = "OK" + +[connection signal="close_requested" from="." to="." method="_on_close_requested"] +[connection signal="pressed" from="PanelContainer/VBoxContainer/Button" to="." method="_on_button_pressed"] diff --git a/godot/scenes/tab/canvas_tab.tscn b/godot/scenes/tab/canvas_tab.tscn new file mode 100644 index 0000000..9e7eb47 --- /dev/null +++ b/godot/scenes/tab/canvas_tab.tscn @@ -0,0 +1,432 @@ +[gd_scene load_steps=35 format=3 uid="uid://byc66jalp35s"] + +[ext_resource type="Script" path="res://src/tab/canvas_tab.gd" id="1_ncx2q"] +[ext_resource type="Texture2D" uid="uid://dfnl502akgfg8" path="res://icons/font_awesome/square-solid.svg" id="2_tlqyv"] +[ext_resource type="Shader" path="res://shaders/fill.gdshader" id="3_eswo4"] +[ext_resource type="Texture2D" uid="uid://07j3j13jje5v" path="res://icons/font_awesome/pen-nib-solid.svg" id="5_sym1t"] +[ext_resource type="Texture2D" uid="uid://cdcr07emv7u2m" path="res://icons/font_awesome/location-arrow-solid.svg" id="6_q1bn5"] +[ext_resource type="Texture2D" uid="uid://oqlqlwg6cqcm" path="res://icons/font_awesome/wrench-solid.svg" id="7_mdo1i"] +[ext_resource type="Texture2D" uid="uid://yopfar782wsm" path="res://icons/font_awesome/expand-solid.svg" id="8_wm1fp"] +[ext_resource type="Texture2D" uid="uid://bej0ujfdimntj" path="res://icons/font_awesome/magnifying-glass-solid.svg" id="9_cj28u"] +[ext_resource type="Script" path="res://src/node/scroll_space.gd" id="9_o6lqk"] +[ext_resource type="Texture2D" uid="uid://dyvcxrvw3yagv" path="res://icons/mirror.svg" id="9_xaqig"] +[ext_resource type="Texture2D" uid="uid://bsxn6okjahtn5" path="res://icons/mirror-flip.svg" id="10_tov58"] +[ext_resource type="PackedScene" uid="uid://cd6xjiluvcmfe" path="res://scenes/control/control_transform.tscn" id="11_ky1kn"] +[ext_resource type="Shader" path="res://shaders/texture_checker_alpha.gdshader" id="11_ri1os"] +[ext_resource type="PackedScene" uid="uid://be2jf3flgfqum" path="res://scenes/control/control_linear_gradient.tscn" id="14_4tfv1"] +[ext_resource type="PackedScene" uid="uid://blrkftxov4tvd" path="res://scenes/control/control_radial_gradient.tscn" id="15_u081m"] + +[sub_resource type="Theme" id="Theme_ojja4"] + +[sub_resource type="InputEventAction" id="InputEventAction_dm14j"] +action = &"drawing_tool" + +[sub_resource type="Shortcut" id="Shortcut_e3cxh"] +events = [SubResource("InputEventAction_dm14j")] + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_jp0pb"] +shader = ExtResource("3_eswo4") +shader_parameter/fill_color = Color(1, 1, 1, 1) + +[sub_resource type="InputEventAction" id="InputEventAction_uy78h"] +action = &"manipulate_tool" + +[sub_resource type="Shortcut" id="Shortcut_fdgit"] +events = [SubResource("InputEventAction_uy78h")] + +[sub_resource type="InputEventAction" id="InputEventAction_x80ry"] +action = &"parameter_tuning_tool" + +[sub_resource type="Shortcut" id="Shortcut_s8d5o"] +events = [SubResource("InputEventAction_x80ry")] + +[sub_resource type="InputEventAction" id="InputEventAction_76oil"] +action = &"expand" + +[sub_resource type="Shortcut" id="Shortcut_7bhdh"] +events = [SubResource("InputEventAction_76oil")] + +[sub_resource type="InputEventAction" id="InputEventAction_671x0"] +action = &"zoom_reset" + +[sub_resource type="Shortcut" id="Shortcut_anip2"] +events = [SubResource("InputEventAction_671x0")] + +[sub_resource type="InputEventAction" id="InputEventAction_kxvvb"] +action = &"mirror" + +[sub_resource type="Shortcut" id="Shortcut_ebprw"] +events = [SubResource("InputEventAction_kxvvb")] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_3kpkk"] + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_j051o"] +shader = ExtResource("11_ri1os") +shader_parameter/document_size = Vector2(780, 720) +shader_parameter/document_scale = 1.0 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_8044c"] + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_i2io4"] +shader = ExtResource("3_eswo4") +shader_parameter/fill_color = Color(1, 1, 1, 0) + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_jwqrm"] +bg_color = Color(0, 0.5, 0.4, 0.313726) +border_width_left = 3 +border_width_top = 3 +border_width_right = 3 +border_width_bottom = 3 +border_color = Color(0, 1, 0.8, 1) + +[node name="CanvasTab" type="PanelContainer"] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 +script = ExtResource("1_ncx2q") + +[node name="canvas" type="HBoxContainer" parent="."] +layout_mode = 2 +mouse_filter = 2 + +[node name="ToolsPanel" type="PanelContainer" parent="canvas"] +layout_mode = 2 +theme = SubResource("Theme_ojja4") + +[node name="ToolsStack" type="VBoxContainer" parent="canvas/ToolsPanel"] +layout_mode = 2 + +[node name="DrawingButton" type="Button" parent="canvas/ToolsPanel/ToolsStack"] +unique_name_in_owner = true +custom_minimum_size = Vector2(48, 48) +layout_mode = 2 +size_flags_horizontal = 4 +size_flags_vertical = 4 +tooltip_text = "TOOLTIP_DRAWING_TOOL" +focus_mode = 0 +shortcut = SubResource("Shortcut_e3cxh") +icon_alignment = 1 +expand_icon = true + +[node name="ToolHighlight" type="Sprite2D" parent="canvas/ToolsPanel/ToolsStack/DrawingButton"] +position = Vector2(24, 24) +scale = Vector2(0.1, 0.1) +texture = ExtResource("2_tlqyv") + +[node name="Sprite2D" type="Sprite2D" parent="canvas/ToolsPanel/ToolsStack/DrawingButton"] +material = SubResource("ShaderMaterial_jp0pb") +position = Vector2(24, 24) +scale = Vector2(0.04, 0.04) +texture = ExtResource("5_sym1t") + +[node name="Label" type="Label" parent="canvas/ToolsPanel/ToolsStack/DrawingButton"] +layout_mode = 1 +anchors_preset = 3 +anchor_left = 1.0 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = -40.0 +offset_top = -23.0 +grow_horizontal = 0 +grow_vertical = 0 +theme_override_font_sizes/font_size = 12 +text = "1" +horizontal_alignment = 2 +vertical_alignment = 2 + +[node name="ManipulateButton" type="Button" parent="canvas/ToolsPanel/ToolsStack"] +unique_name_in_owner = true +custom_minimum_size = Vector2(48, 48) +layout_mode = 2 +size_flags_horizontal = 4 +size_flags_vertical = 4 +tooltip_text = "TOOLTIP_MANIPULATE_TOOL" +focus_mode = 0 +shortcut = SubResource("Shortcut_fdgit") +icon_alignment = 1 +expand_icon = true + +[node name="ToolHighlight" type="Sprite2D" parent="canvas/ToolsPanel/ToolsStack/ManipulateButton"] +visible = false +position = Vector2(24, 24) +scale = Vector2(0.1, 0.1) +texture = ExtResource("2_tlqyv") + +[node name="Sprite2D" type="Sprite2D" parent="canvas/ToolsPanel/ToolsStack/ManipulateButton"] +material = SubResource("ShaderMaterial_jp0pb") +position = Vector2(24, 24) +rotation = -1.5708 +scale = Vector2(0.04, 0.04) +texture = ExtResource("6_q1bn5") + +[node name="Label" type="Label" parent="canvas/ToolsPanel/ToolsStack/ManipulateButton"] +layout_mode = 1 +anchors_preset = 3 +anchor_left = 1.0 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = -40.0 +offset_top = -23.0 +grow_horizontal = 0 +grow_vertical = 0 +theme_override_font_sizes/font_size = 12 +text = "2" +horizontal_alignment = 2 +vertical_alignment = 2 + +[node name="ParameterTuningButton" type="Button" parent="canvas/ToolsPanel/ToolsStack"] +unique_name_in_owner = true +custom_minimum_size = Vector2(48, 48) +layout_mode = 2 +size_flags_horizontal = 4 +size_flags_vertical = 4 +tooltip_text = "TOOLTIP_PARAMETER_TUNING_TOOL" +focus_mode = 0 +shortcut = SubResource("Shortcut_s8d5o") +icon_alignment = 1 +expand_icon = true + +[node name="ToolHighlight" type="Sprite2D" parent="canvas/ToolsPanel/ToolsStack/ParameterTuningButton"] +visible = false +position = Vector2(24, 24) +scale = Vector2(0.1, 0.1) +texture = ExtResource("2_tlqyv") + +[node name="Sprite2D" type="Sprite2D" parent="canvas/ToolsPanel/ToolsStack/ParameterTuningButton"] +material = SubResource("ShaderMaterial_jp0pb") +position = Vector2(24, 24) +rotation = -1.5708 +scale = Vector2(0.04, 0.04) +texture = ExtResource("7_mdo1i") + +[node name="Label" type="Label" parent="canvas/ToolsPanel/ToolsStack/ParameterTuningButton"] +layout_mode = 1 +anchors_preset = 3 +anchor_left = 1.0 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = -40.0 +offset_top = -23.0 +grow_horizontal = 0 +grow_vertical = 0 +theme_override_font_sizes/font_size = 12 +text = "3" +horizontal_alignment = 2 +vertical_alignment = 2 + +[node name="ExpandButton" type="Button" parent="canvas/ToolsPanel/ToolsStack"] +custom_minimum_size = Vector2(48, 48) +layout_mode = 2 +size_flags_horizontal = 4 +size_flags_vertical = 4 +tooltip_text = "TOOLTIP_EXPAND" +focus_mode = 0 +shortcut = SubResource("Shortcut_7bhdh") +icon_alignment = 1 +expand_icon = true + +[node name="Sprite2D" type="Sprite2D" parent="canvas/ToolsPanel/ToolsStack/ExpandButton"] +material = SubResource("ShaderMaterial_jp0pb") +position = Vector2(24, 24) +scale = Vector2(0.04, 0.04) +texture = ExtResource("8_wm1fp") + +[node name="Label" type="Label" parent="canvas/ToolsPanel/ToolsStack/ExpandButton"] +layout_mode = 1 +anchors_preset = 3 +anchor_left = 1.0 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = -40.0 +offset_top = -23.0 +grow_horizontal = 0 +grow_vertical = 0 +theme_override_font_sizes/font_size = 12 +text = "4" +horizontal_alignment = 2 +vertical_alignment = 2 + +[node name="ZoomResetButton" type="Button" parent="canvas/ToolsPanel/ToolsStack"] +custom_minimum_size = Vector2(48, 48) +layout_mode = 2 +size_flags_horizontal = 4 +size_flags_vertical = 4 +tooltip_text = "TOOLTIP_RESET_ZOOM" +focus_mode = 0 +shortcut = SubResource("Shortcut_anip2") +icon_alignment = 1 +expand_icon = true + +[node name="Sprite2D" type="Sprite2D" parent="canvas/ToolsPanel/ToolsStack/ZoomResetButton"] +material = SubResource("ShaderMaterial_jp0pb") +position = Vector2(24, 24) +scale = Vector2(0.05, 0.05) +texture = ExtResource("9_cj28u") + +[node name="square" type="Sprite2D" parent="canvas/ToolsPanel/ToolsStack/ZoomResetButton"] +material = SubResource("ShaderMaterial_jp0pb") +position = Vector2(21.5, 21.5) +scale = Vector2(0.02, 0.02) +texture = ExtResource("2_tlqyv") + +[node name="Label" type="Label" parent="canvas/ToolsPanel/ToolsStack/ZoomResetButton"] +layout_mode = 1 +anchors_preset = 3 +anchor_left = 1.0 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = -40.0 +offset_top = -23.0 +grow_horizontal = 0 +grow_vertical = 0 +theme_override_font_sizes/font_size = 12 +text = "5" +horizontal_alignment = 2 +vertical_alignment = 2 + +[node name="MirrorButton" type="Button" parent="canvas/ToolsPanel/ToolsStack"] +custom_minimum_size = Vector2(48, 48) +layout_mode = 2 +size_flags_horizontal = 4 +size_flags_vertical = 4 +tooltip_text = "TOOLTIP_MIRROR" +focus_mode = 0 +shortcut = SubResource("Shortcut_ebprw") +icon_alignment = 1 +expand_icon = true + +[node name="MirrorSprite" type="Sprite2D" parent="canvas/ToolsPanel/ToolsStack/MirrorButton"] +unique_name_in_owner = true +material = SubResource("ShaderMaterial_jp0pb") +position = Vector2(24, 24) +scale = Vector2(0.06, 0.06) +texture = ExtResource("9_xaqig") + +[node name="MirrorFlipSprite" type="Sprite2D" parent="canvas/ToolsPanel/ToolsStack/MirrorButton"] +unique_name_in_owner = true +visible = false +material = SubResource("ShaderMaterial_jp0pb") +position = Vector2(24, 24) +scale = Vector2(0.06, 0.06) +texture = ExtResource("10_tov58") + +[node name="Label" type="Label" parent="canvas/ToolsPanel/ToolsStack/MirrorButton"] +layout_mode = 1 +anchors_preset = 3 +anchor_left = 1.0 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = -40.0 +offset_top = -23.0 +grow_horizontal = 0 +grow_vertical = 0 +theme_override_font_sizes/font_size = 12 +text = "6" +horizontal_alignment = 2 +vertical_alignment = 2 + +[node name="ScrollSpace" type="Panel" parent="canvas"] +unique_name_in_owner = true +clip_contents = true +layout_mode = 2 +size_flags_horizontal = 3 +mouse_filter = 2 +theme_override_styles/panel = SubResource("StyleBoxEmpty_3kpkk") +script = ExtResource("9_o6lqk") + +[node name="Document" type="Panel" parent="canvas/ScrollSpace"] +visible = false +material = SubResource("ShaderMaterial_j051o") +clip_contents = true +layout_mode = 0 +offset_right = 480.0 +offset_bottom = 720.0 +mouse_filter = 2 +theme_override_styles/panel = SubResource("StyleBoxFlat_8044c") + +[node name="ControlLayer" type="Panel" parent="canvas/ScrollSpace"] +visible = false +material = SubResource("ShaderMaterial_i2io4") +layout_mode = 0 +offset_right = 480.0 +offset_bottom = 720.0 +mouse_filter = 2 +theme_override_styles/panel = SubResource("StyleBoxFlat_8044c") + +[node name="ControlTransform" parent="canvas/ScrollSpace/ControlLayer" instance=ExtResource("11_ky1kn")] +unique_name_in_owner = true +visible = false +layout_mode = 0 + +[node name="ControlLinearGradient" parent="canvas/ScrollSpace/ControlLayer" instance=ExtResource("14_4tfv1")] +unique_name_in_owner = true +visible = false + +[node name="ControlRadialGradient" parent="canvas/ScrollSpace/ControlLayer" instance=ExtResource("15_u081m")] +unique_name_in_owner = true +visible = false + +[node name="DragAreaPanel" type="Panel" parent="canvas/ScrollSpace"] +visible = false +layout_mode = 0 +offset_right = 40.0 +offset_bottom = 40.0 +theme_override_styles/panel = SubResource("StyleBoxFlat_jwqrm") + +[node name="ParameterPanel" type="Panel" parent="canvas/ScrollSpace"] +unique_name_in_owner = true +visible = false +clip_contents = true +layout_mode = 1 +anchors_preset = 2 +anchor_top = 1.0 +anchor_bottom = 1.0 +offset_top = -60.0 +offset_right = 120.0 +grow_vertical = 0 + +[node name="VBoxContainer" type="VBoxContainer" parent="canvas/ScrollSpace/ParameterPanel"] +layout_mode = 1 +anchors_preset = 4 +anchor_top = 0.5 +anchor_bottom = 0.5 +offset_left = 10.0 +offset_top = -20.0 +offset_right = 52.0 +offset_bottom = 20.0 +grow_vertical = 2 + +[node name="ParameterLabel1" type="Label" parent="canvas/ScrollSpace/ParameterPanel/VBoxContainer"] +unique_name_in_owner = true +auto_translate_mode = 2 +layout_mode = 2 +localize_numeral_system = false +theme_override_colors/font_color = Color(1, 1, 0, 1) +theme_override_font_sizes/font_size = 12 +text = "Phi: 1.0" + +[node name="ParameterLabel2" type="Label" parent="canvas/ScrollSpace/ParameterPanel/VBoxContainer"] +unique_name_in_owner = true +auto_translate_mode = 2 +layout_mode = 2 +localize_numeral_system = false +theme_override_colors/font_color = Color(1, 1, 0, 1) +theme_override_font_sizes/font_size = 12 +text = "Psi: 1.0" + +[connection signal="pressed" from="canvas/ToolsPanel/ToolsStack/DrawingButton" to="." method="_on_drawing_button_pressed"] +[connection signal="pressed" from="canvas/ToolsPanel/ToolsStack/ManipulateButton" to="." method="_on_direct_selection_button_pressed"] +[connection signal="pressed" from="canvas/ToolsPanel/ToolsStack/ParameterTuningButton" to="." method="_on_parameter_tuning_button_pressed"] +[connection signal="pressed" from="canvas/ToolsPanel/ToolsStack/ExpandButton" to="." method="_on_expand_button_pressed"] +[connection signal="pressed" from="canvas/ToolsPanel/ToolsStack/ZoomResetButton" to="." method="_on_zoom_reset_button_pressed"] +[connection signal="pressed" from="canvas/ToolsPanel/ToolsStack/MirrorButton" to="." method="_on_mirror_button_pressed"] +[connection signal="on_manipulate_finished" from="canvas/ScrollSpace/ControlLayer/ControlTransform" to="." method="_on_control_transform_on_manipulate_finished"] diff --git a/godot/scenes/tab/layer_list_tab.tscn b/godot/scenes/tab/layer_list_tab.tscn new file mode 100644 index 0000000..4e112b6 --- /dev/null +++ b/godot/scenes/tab/layer_list_tab.tscn @@ -0,0 +1,317 @@ +[gd_scene load_steps=23 format=3 uid="uid://dcihb270dgr70"] + +[ext_resource type="Script" path="res://src/tab/layer_list_tab.gd" id="1_e2s20"] +[ext_resource type="Shader" path="res://shaders/fill.gdshader" id="1_hktsu"] +[ext_resource type="Texture2D" uid="uid://bihym07yhfwvb" path="res://icons/font_awesome/caret-down-solid.svg" id="1_nxau0"] +[ext_resource type="Texture2D" uid="uid://cfy4c56trjpcp" path="res://icons/font_awesome/trash-solid.svg" id="3_l014w"] +[ext_resource type="Texture2D" uid="uid://eej4fsmgxa1d" path="res://icons/square-plus.svg" id="4_6kt4j"] +[ext_resource type="Texture2D" uid="uid://clwpv24en3js7" path="res://icons/fill-plus.svg" id="4_gli0p"] +[ext_resource type="Texture2D" uid="uid://dsgcgfj17p0ih" path="res://icons/folder-plus.svg" id="5_2nysa"] +[ext_resource type="Texture2D" uid="uid://ukf4j5kwqbqc" path="res://icons/font_awesome/clone-regular.svg" id="6_jqgq0"] +[ext_resource type="Texture2D" uid="uid://mrvjp3pgu7ea" path="res://icons/open_path.svg" id="7_ng5sb"] +[ext_resource type="Texture2D" uid="uid://bkls8jpsc3ps5" path="res://icons/union.svg" id="7_ue8sd"] +[ext_resource type="Texture2D" uid="uid://dc2f1ibod6vkx" path="res://icons/font_awesome/lock-open-solid.svg" id="7_vwbx8"] +[ext_resource type="Texture2D" uid="uid://bgaugxuhl5lvm" path="res://icons/font_awesome/lock-solid.svg" id="8_fb4p3"] +[ext_resource type="Texture2D" uid="uid://esb812dxjhx3" path="res://icons/diff.svg" id="8_s2v0l"] +[ext_resource type="Texture2D" uid="uid://b5qkp4rnmh7p5" path="res://icons/closed_path.svg" id="8_uys6j"] +[ext_resource type="Texture2D" uid="uid://delwiq2ud4o5b" path="res://icons/intersect.svg" id="9_yxned"] +[ext_resource type="Texture2D" uid="uid://bsxiknl4vq61y" path="res://icons/xor.svg" id="10_56f02"] +[ext_resource type="PackedScene" uid="uid://83wedgkgqa8o" path="res://scenes/node/list_drop_cursor.tscn" id="16_y34j8"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_pn8me"] +content_margin_left = 2.0 +content_margin_top = 6.0 +content_margin_right = 2.0 +content_margin_bottom = 0.0 +bg_color = Color(0.6, 0.6, 0.6, 0.392157) + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_amtw7"] +shader = ExtResource("1_hktsu") +shader_parameter/fill_color = Color(1, 1, 1, 1) + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_tcfkd"] +shader = ExtResource("1_hktsu") +shader_parameter/fill_color = Color(1, 1, 1, 1) + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_gjcem"] +bg_color = Color(1, 1, 1, 0.705882) +border_width_left = 2 +border_width_top = 2 +border_width_right = 2 +border_width_bottom = 2 +border_color = Color(1, 1, 1, 1) +corner_radius_top_left = 2 +corner_radius_top_right = 2 +corner_radius_bottom_right = 2 +corner_radius_bottom_left = 2 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_00u5q"] +content_margin_top = 2.0 +content_margin_bottom = 2.0 +bg_color = Color(0.6, 0.6, 0.6, 0) + +[node name="LayerListTab" type="PanelContainer"] +custom_minimum_size = Vector2(320, 0) +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_e2s20") + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +layout_mode = 2 +theme_override_constants/separation = 0 + +[node name="PanelContainer" type="PanelContainer" parent="VBoxContainer"] +layout_mode = 2 +theme_override_styles/panel = SubResource("StyleBoxFlat_pn8me") + +[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/PanelContainer"] +layout_mode = 2 + +[node name="LayerController" type="HBoxContainer" parent="VBoxContainer/PanelContainer/VBoxContainer"] +layout_mode = 2 + +[node name="BlendModeMenu" type="MenuButton" parent="VBoxContainer/PanelContainer/VBoxContainer/LayerController"] +unique_name_in_owner = true +custom_minimum_size = Vector2(160, 0) +layout_mode = 2 +tooltip_text = "TOOLTIP_BLEND_MODE" +text = "BLEND_MODE_NORMAL" +flat = false +expand_icon = true +item_count = 5 +popup/item_0/text = "BLEND_MODE_NORMAL" +popup/item_1/text = "BLEND_MODE_ADD" +popup/item_1/id = 1 +popup/item_2/text = "BLEND_MODE_MULTIPLY" +popup/item_2/id = 2 +popup/item_3/text = "BLEND_MODE_SCREEN" +popup/item_3/id = 3 +popup/item_4/text = "BLEND_MODE_OVERLAY" +popup/item_4/id = 4 + +[node name="Caret" type="Sprite2D" parent="VBoxContainer/PanelContainer/VBoxContainer/LayerController/BlendModeMenu"] +material = SubResource("ShaderMaterial_amtw7") +position = Vector2(148, 16) +scale = Vector2(0.03, 0.03) +texture = ExtResource("1_nxau0") + +[node name="AlphaSlider" type="HSlider" parent="VBoxContainer/PanelContainer/VBoxContainer/LayerController"] +unique_name_in_owner = true +custom_minimum_size = Vector2(60, 0) +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 4 +tooltip_text = "TOOLTIP_ALPHA" +value = 100.0 + +[node name="AlphaSpinBox" type="SpinBox" parent="VBoxContainer/PanelContainer/VBoxContainer/LayerController"] +unique_name_in_owner = true +layout_mode = 2 +tooltip_text = "TOOLTIP_ALPHA" +value = 100.0 +select_all_on_focus = true + +[node name="Buttons" type="HBoxContainer" parent="VBoxContainer/PanelContainer/VBoxContainer"] +layout_mode = 2 + +[node name="AddButton" type="Button" parent="VBoxContainer/PanelContainer/VBoxContainer/Buttons"] +unique_name_in_owner = true +custom_minimum_size = Vector2(32, 32) +layout_mode = 2 +tooltip_text = "TOOLTIP_NEW_PATH_LAYER" +focus_mode = 0 + +[node name="Sprite2D" type="Sprite2D" parent="VBoxContainer/PanelContainer/VBoxContainer/Buttons/AddButton"] +material = SubResource("ShaderMaterial_tcfkd") +position = Vector2(16, 16) +texture = ExtResource("4_6kt4j") + +[node name="AddFillButton" type="Button" parent="VBoxContainer/PanelContainer/VBoxContainer/Buttons"] +unique_name_in_owner = true +custom_minimum_size = Vector2(32, 32) +layout_mode = 2 +tooltip_text = "TOOLTIP_NEW_FILL_LAYER" +focus_mode = 0 + +[node name="Sprite2D" type="Sprite2D" parent="VBoxContainer/PanelContainer/VBoxContainer/Buttons/AddFillButton"] +material = SubResource("ShaderMaterial_tcfkd") +position = Vector2(16, 16) +texture = ExtResource("4_gli0p") + +[node name="AddGroupButton" type="Button" parent="VBoxContainer/PanelContainer/VBoxContainer/Buttons"] +unique_name_in_owner = true +custom_minimum_size = Vector2(32, 32) +layout_mode = 2 +tooltip_text = "TOOLTIP_NEW_GROUP_LAYER" +focus_mode = 0 + +[node name="Sprite2D" type="Sprite2D" parent="VBoxContainer/PanelContainer/VBoxContainer/Buttons/AddGroupButton"] +material = SubResource("ShaderMaterial_tcfkd") +position = Vector2(16, 16) +texture = ExtResource("5_2nysa") + +[node name="Space" type="ColorRect" parent="VBoxContainer/PanelContainer/VBoxContainer/Buttons"] +custom_minimum_size = Vector2(16, 0) +layout_mode = 2 +size_flags_horizontal = 3 +color = Color(1, 1, 1, 0) + +[node name="DeleteButton" type="Button" parent="VBoxContainer/PanelContainer/VBoxContainer/Buttons"] +unique_name_in_owner = true +custom_minimum_size = Vector2(32, 32) +layout_mode = 2 +tooltip_text = "TOOLTIP_DELETE_LAYER" +focus_mode = 0 + +[node name="Sprite2D" type="Sprite2D" parent="VBoxContainer/PanelContainer/VBoxContainer/Buttons/DeleteButton"] +material = SubResource("ShaderMaterial_tcfkd") +position = Vector2(16, 16) +scale = Vector2(0.03, 0.03) +texture = ExtResource("3_l014w") + +[node name="ClipButton" type="Button" parent="VBoxContainer/PanelContainer/VBoxContainer/Buttons"] +unique_name_in_owner = true +custom_minimum_size = Vector2(32, 32) +layout_mode = 2 +tooltip_text = "TOOLTIP_CLIPPING" +focus_mode = 0 +toggle_mode = true + +[node name="Sprite2D" type="Sprite2D" parent="VBoxContainer/PanelContainer/VBoxContainer/Buttons/ClipButton"] +material = SubResource("ShaderMaterial_tcfkd") +position = Vector2(16, 16) +scale = Vector2(0.04, 0.04) +texture = ExtResource("6_jqgq0") + +[node name="Panel" type="Panel" parent="VBoxContainer/PanelContainer/VBoxContainer/Buttons/ClipButton"] +visible = false +layout_mode = 0 +offset_right = 32.0 +offset_bottom = 32.0 +mouse_filter = 2 +theme_override_styles/panel = SubResource("StyleBoxFlat_gjcem") + +[node name="LockButton" type="Button" parent="VBoxContainer/PanelContainer/VBoxContainer/Buttons"] +unique_name_in_owner = true +custom_minimum_size = Vector2(32, 32) +layout_mode = 2 +tooltip_text = "TOOLTIP_LAYER_LOCK" +focus_mode = 0 + +[node name="UnLocked" type="Sprite2D" parent="VBoxContainer/PanelContainer/VBoxContainer/Buttons/LockButton"] +material = SubResource("ShaderMaterial_tcfkd") +position = Vector2(16, 16) +scale = Vector2(0.03, 0.03) +texture = ExtResource("7_vwbx8") + +[node name="Locked" type="Sprite2D" parent="VBoxContainer/PanelContainer/VBoxContainer/Buttons/LockButton"] +visible = false +material = SubResource("ShaderMaterial_tcfkd") +position = Vector2(16, 16) +scale = Vector2(0.03, 0.03) +texture = ExtResource("8_fb4p3") + +[node name="Panel" type="Panel" parent="VBoxContainer/PanelContainer/VBoxContainer/Buttons/LockButton"] +visible = false +layout_mode = 0 +offset_right = 32.0 +offset_bottom = 32.0 +mouse_filter = 2 +theme_override_styles/panel = SubResource("StyleBoxFlat_gjcem") + +[node name="OpenCloseButton" type="Button" parent="VBoxContainer/PanelContainer/VBoxContainer/Buttons"] +unique_name_in_owner = true +custom_minimum_size = Vector2(32, 32) +layout_mode = 2 +tooltip_text = "TOOLTIP_PATH_OPEN_CLOSE" +focus_mode = 0 + +[node name="Open" type="Sprite2D" parent="VBoxContainer/PanelContainer/VBoxContainer/Buttons/OpenCloseButton"] +material = SubResource("ShaderMaterial_tcfkd") +position = Vector2(16, 16) +scale = Vector2(0.03, 0.03) +texture = ExtResource("7_ng5sb") + +[node name="Close" type="Sprite2D" parent="VBoxContainer/PanelContainer/VBoxContainer/Buttons/OpenCloseButton"] +visible = false +material = SubResource("ShaderMaterial_tcfkd") +position = Vector2(16, 16) +scale = Vector2(0.03, 0.03) +texture = ExtResource("8_uys6j") + +[node name="BooleanMenu" type="MenuButton" parent="VBoxContainer/PanelContainer/VBoxContainer/Buttons"] +unique_name_in_owner = true +custom_minimum_size = Vector2(48, 0) +layout_mode = 2 +tooltip_text = "TOOLTIP_BOOLEAN" +flat = false +item_count = 4 +popup/item_0/text = "BOOLEAN_UNION" +popup/item_0/icon = ExtResource("7_ue8sd") +popup/item_1/text = "BOOLEAN_DIFF" +popup/item_1/icon = ExtResource("8_s2v0l") +popup/item_1/id = 1 +popup/item_2/text = "BOOLEAN_INTERSECT" +popup/item_2/icon = ExtResource("9_yxned") +popup/item_2/id = 2 +popup/item_3/text = "BOOLEAN_XOR" +popup/item_3/icon = ExtResource("10_56f02") +popup/item_3/id = 3 + +[node name="Union" type="Sprite2D" parent="VBoxContainer/PanelContainer/VBoxContainer/Buttons/BooleanMenu"] +position = Vector2(16, 16) +texture = ExtResource("7_ue8sd") + +[node name="Diff" type="Sprite2D" parent="VBoxContainer/PanelContainer/VBoxContainer/Buttons/BooleanMenu"] +visible = false +position = Vector2(16, 16) +texture = ExtResource("8_s2v0l") + +[node name="Intersect" type="Sprite2D" parent="VBoxContainer/PanelContainer/VBoxContainer/Buttons/BooleanMenu"] +visible = false +position = Vector2(16, 16) +texture = ExtResource("9_yxned") + +[node name="Xor" type="Sprite2D" parent="VBoxContainer/PanelContainer/VBoxContainer/Buttons/BooleanMenu"] +visible = false +position = Vector2(16, 16) +texture = ExtResource("10_56f02") + +[node name="Caret" type="Sprite2D" parent="VBoxContainer/PanelContainer/VBoxContainer/Buttons/BooleanMenu"] +material = SubResource("ShaderMaterial_amtw7") +position = Vector2(36, 16) +scale = Vector2(0.03, 0.03) +texture = ExtResource("1_nxau0") + +[node name="HSeparator" type="HSeparator" parent="VBoxContainer/PanelContainer/VBoxContainer"] +layout_mode = 2 + +[node name="ScrollContainer" type="ScrollContainer" parent="VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_vertical = 3 +theme_override_styles/panel = SubResource("StyleBoxFlat_00u5q") +horizontal_scroll_mode = 0 + +[node name="ListContainer" type="VBoxContainer" parent="VBoxContainer/ScrollContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_constants/separation = 0 + +[node name="ListDropCursor" parent="VBoxContainer/ScrollContainer/ListContainer" instance=ExtResource("16_y34j8")] +visible = false +layout_mode = 2 + +[connection signal="value_changed" from="VBoxContainer/PanelContainer/VBoxContainer/LayerController/AlphaSlider" to="." method="_on_alpha_slider_value_changed"] +[connection signal="value_changed" from="VBoxContainer/PanelContainer/VBoxContainer/LayerController/AlphaSpinBox" to="." method="_on_alpha_spin_box_value_changed"] +[connection signal="button_up" from="VBoxContainer/PanelContainer/VBoxContainer/Buttons/AddButton" to="." method="_on_add_button_button_up"] +[connection signal="button_up" from="VBoxContainer/PanelContainer/VBoxContainer/Buttons/AddFillButton" to="." method="_on_add_fill_button_button_up"] +[connection signal="button_up" from="VBoxContainer/PanelContainer/VBoxContainer/Buttons/AddGroupButton" to="." method="_on_add_group_button_button_up"] +[connection signal="button_up" from="VBoxContainer/PanelContainer/VBoxContainer/Buttons/DeleteButton" to="." method="_on_delete_button_button_up"] +[connection signal="button_up" from="VBoxContainer/PanelContainer/VBoxContainer/Buttons/ClipButton" to="." method="_on_clip_button_button_up"] +[connection signal="button_up" from="VBoxContainer/PanelContainer/VBoxContainer/Buttons/LockButton" to="." method="_on_lock_button_button_up"] +[connection signal="button_up" from="VBoxContainer/PanelContainer/VBoxContainer/Buttons/OpenCloseButton" to="." method="_on_open_close_button_button_up"] diff --git a/godot/scenes/tab/layer_setting_tab.tscn b/godot/scenes/tab/layer_setting_tab.tscn new file mode 100644 index 0000000..13fe751 --- /dev/null +++ b/godot/scenes/tab/layer_setting_tab.tscn @@ -0,0 +1,406 @@ +[gd_scene load_steps=27 format=3 uid="uid://c8ge45ar23ppv"] + +[ext_resource type="Script" path="res://src/tab/layer_setting_tab.gd" id="1_mebqs"] +[ext_resource type="Shader" path="res://shaders/missing_material.gdshader" id="2_8cncw"] +[ext_resource type="StyleBox" uid="uid://cvvu0fweitcpr" path="res://styles/flat_color_button.tres" id="2_cxx1c"] +[ext_resource type="Shader" path="res://shaders/linear_gradient_checker_alpha.gdshader" id="3_rloo5"] +[ext_resource type="Shader" path="res://shaders/fill.gdshader" id="3_xqoo3"] +[ext_resource type="Texture2D" uid="uid://w3jj8dsk5ee" path="res://icons/font_awesome/square-regular.svg" id="4_87elw"] +[ext_resource type="Shader" path="res://shaders/radial_gradient_checker_alpha.gdshader" id="5_jy12q"] +[ext_resource type="Texture2D" uid="uid://byheik8rybqlc" path="res://icons/font_awesome/slash-solid.svg" id="5_p8u4y"] +[ext_resource type="Texture2D" uid="uid://dfnl502akgfg8" path="res://icons/font_awesome/square-solid.svg" id="6_wg8l6"] +[ext_resource type="PackedScene" uid="uid://bctakxrfgwaor" path="res://scenes/node/material_select_popup.tscn" id="7_3ul3g"] +[ext_resource type="Shader" path="res://shaders/fill_checker_alpha.gdshader" id="7_rkdh5"] + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_0bfa8"] +shader = ExtResource("2_8cncw") + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_4a6yf"] +shader = ExtResource("7_rkdh5") +shader_parameter/fill_color = Color(1, 1, 1, 1) +shader_parameter/document_size = Vector2(64, 24) +shader_parameter/document_scale = 1.0 + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_bl5ba"] +shader = ExtResource("3_rloo5") +shader_parameter/document_size = Vector2(64, 24) +shader_parameter/document_scale = 1.0 + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_ahgsv"] +shader = ExtResource("5_jy12q") +shader_parameter/document_size = Vector2(64, 24) +shader_parameter/document_scale = 1.0 + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_wh8b0"] +shader = ExtResource("3_xqoo3") +shader_parameter/fill_color = Color(1, 1, 1, 1) + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_sg8hq"] +shader = ExtResource("3_xqoo3") +shader_parameter/fill_color = Color(1, 1, 1, 1) + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_i7x5w"] +bg_color = Color(0, 0, 0, 0.784314) +corner_radius_top_left = 3 +corner_radius_top_right = 3 +corner_radius_bottom_right = 3 +corner_radius_bottom_left = 3 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_s8urq"] +bg_color = Color(0, 0, 0, 0.784314) +corner_radius_top_left = 3 +corner_radius_top_right = 3 +corner_radius_bottom_right = 3 +corner_radius_bottom_left = 3 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_5xoro"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_2syiy"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_dkh1d"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_4auj0"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_ycrbb"] + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_jic5n"] +shader = ExtResource("7_rkdh5") +shader_parameter/fill_color = Color(1, 1, 1, 0) +shader_parameter/document_size = Vector2(64, 24) +shader_parameter/document_scale = 1.0 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_tgaui"] +corner_radius_top_left = 3 +corner_radius_top_right = 3 +corner_radius_bottom_right = 3 +corner_radius_bottom_left = 3 + +[node name="LayerSetting" type="PanelContainer"] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_mebqs") +_missing_material = SubResource("ShaderMaterial_0bfa8") +_color_material = SubResource("ShaderMaterial_4a6yf") +_linear_gradient_material = SubResource("ShaderMaterial_bl5ba") +_radial_gradient_material = SubResource("ShaderMaterial_ahgsv") + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +layout_mode = 2 +theme_override_constants/separation = 4 + +[node name="FillMaterial" type="HBoxContainer" parent="VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_vertical = 4 +theme_override_constants/separation = 4 + +[node name="Label" type="Label" parent="VBoxContainer/FillMaterial"] +custom_minimum_size = Vector2(42, 0) +layout_mode = 2 +text = "LAYER_SETTINGS_FILL" + +[node name="EmptyButton" type="Button" parent="VBoxContainer/FillMaterial"] +clip_contents = true +custom_minimum_size = Vector2(24, 24) +layout_mode = 2 +size_flags_vertical = 4 +tooltip_text = "TOOLTIP_HIDE_FILL_MATERIAL" +focus_mode = 0 +mouse_default_cursor_shape = 2 +theme_override_styles/focus = ExtResource("2_cxx1c") +theme_override_styles/disabled = ExtResource("2_cxx1c") +theme_override_styles/hover = ExtResource("2_cxx1c") +theme_override_styles/pressed = ExtResource("2_cxx1c") +theme_override_styles/normal = ExtResource("2_cxx1c") + +[node name="Sprite2D" type="Sprite2D" parent="VBoxContainer/FillMaterial/EmptyButton"] +material = SubResource("ShaderMaterial_wh8b0") +position = Vector2(12, 12) +scale = Vector2(0.03, 0.03) +texture = ExtResource("4_87elw") + +[node name="Sprite2D2" type="Sprite2D" parent="VBoxContainer/FillMaterial/EmptyButton"] +material = SubResource("ShaderMaterial_sg8hq") +position = Vector2(12, 12) +scale = Vector2(0.03, 0.03) +texture = ExtResource("5_p8u4y") + +[node name="Panel" type="Panel" parent="VBoxContainer/FillMaterial/EmptyButton"] +layout_mode = 0 +offset_right = 24.0 +offset_bottom = 24.0 +mouse_filter = 2 +theme_override_styles/panel = SubResource("StyleBoxFlat_i7x5w") + +[node name="FillButton" type="Button" parent="VBoxContainer/FillMaterial"] +clip_contents = true +custom_minimum_size = Vector2(24, 24) +layout_mode = 2 +size_flags_vertical = 4 +tooltip_text = "TOOLTIP_SHOW_FILL_MATERIAL" +focus_mode = 0 +mouse_default_cursor_shape = 2 +theme_override_styles/focus = ExtResource("2_cxx1c") +theme_override_styles/disabled = ExtResource("2_cxx1c") +theme_override_styles/hover = ExtResource("2_cxx1c") +theme_override_styles/pressed = ExtResource("2_cxx1c") +theme_override_styles/normal = ExtResource("2_cxx1c") + +[node name="Sprite2D" type="Sprite2D" parent="VBoxContainer/FillMaterial/FillButton"] +material = SubResource("ShaderMaterial_wh8b0") +position = Vector2(12, 12) +scale = Vector2(0.03, 0.03) +texture = ExtResource("6_wg8l6") + +[node name="Panel" type="Panel" parent="VBoxContainer/FillMaterial/FillButton"] +layout_mode = 0 +offset_right = 24.0 +offset_bottom = 24.0 +mouse_filter = 2 +theme_override_styles/panel = SubResource("StyleBoxFlat_s8urq") + +[node name="ColorButton" type="Button" parent="VBoxContainer/FillMaterial"] +clip_contents = true +custom_minimum_size = Vector2(64, 32) +layout_mode = 2 +size_flags_horizontal = 3 +tooltip_text = "塗りのマテリアルの選択" +focus_mode = 0 +mouse_default_cursor_shape = 2 +theme_override_styles/focus = SubResource("StyleBoxFlat_5xoro") +theme_override_styles/disabled = SubResource("StyleBoxEmpty_2syiy") +theme_override_styles/hover = SubResource("StyleBoxFlat_dkh1d") +theme_override_styles/pressed = SubResource("StyleBoxEmpty_4auj0") +theme_override_styles/normal = SubResource("StyleBoxEmpty_ycrbb") + +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/FillMaterial/ColorButton"] +layout_mode = 1 +anchors_preset = 4 +anchor_top = 0.5 +anchor_bottom = 0.5 +offset_top = -12.0 +offset_right = 146.0 +offset_bottom = 12.0 +grow_vertical = 2 + +[node name="Color" type="Panel" parent="VBoxContainer/FillMaterial/ColorButton/HBoxContainer"] +material = SubResource("ShaderMaterial_jic5n") +custom_minimum_size = Vector2(64, 0) +layout_mode = 2 +size_flags_horizontal = 0 +mouse_filter = 1 +theme_override_styles/panel = SubResource("StyleBoxFlat_tgaui") + +[node name="Label" type="Label" parent="VBoxContainer/FillMaterial/ColorButton/HBoxContainer"] +auto_translate_mode = 2 +layout_mode = 2 +theme_override_font_sizes/font_size = 12 +text = "Material 0" + +[node name="MaterialSelectPopup" parent="VBoxContainer/FillMaterial/ColorButton" instance=ExtResource("7_3ul3g")] +visible = false + +[node name="LineMaterial" type="HBoxContainer" parent="VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_vertical = 4 +theme_override_constants/separation = 4 + +[node name="Label" type="Label" parent="VBoxContainer/LineMaterial"] +custom_minimum_size = Vector2(42, 0) +layout_mode = 2 +text = "LAYER_SETTINGS_LINE" + +[node name="EmptyButton" type="Button" parent="VBoxContainer/LineMaterial"] +clip_contents = true +custom_minimum_size = Vector2(24, 24) +layout_mode = 2 +size_flags_vertical = 4 +tooltip_text = "TOOLTIP_HIDE_LINE_MATERIAL" +focus_mode = 0 +mouse_default_cursor_shape = 2 +theme_override_styles/focus = ExtResource("2_cxx1c") +theme_override_styles/disabled = ExtResource("2_cxx1c") +theme_override_styles/hover = ExtResource("2_cxx1c") +theme_override_styles/pressed = ExtResource("2_cxx1c") +theme_override_styles/normal = ExtResource("2_cxx1c") + +[node name="Sprite2D" type="Sprite2D" parent="VBoxContainer/LineMaterial/EmptyButton"] +material = SubResource("ShaderMaterial_wh8b0") +position = Vector2(12, 12) +scale = Vector2(0.03, 0.03) +texture = ExtResource("4_87elw") + +[node name="Sprite2D2" type="Sprite2D" parent="VBoxContainer/LineMaterial/EmptyButton"] +material = SubResource("ShaderMaterial_sg8hq") +position = Vector2(12, 12) +scale = Vector2(0.03, 0.03) +texture = ExtResource("5_p8u4y") + +[node name="Panel" type="Panel" parent="VBoxContainer/LineMaterial/EmptyButton"] +layout_mode = 0 +offset_right = 24.0 +offset_bottom = 24.0 +mouse_filter = 2 +theme_override_styles/panel = SubResource("StyleBoxFlat_i7x5w") + +[node name="FillButton" type="Button" parent="VBoxContainer/LineMaterial"] +clip_contents = true +custom_minimum_size = Vector2(24, 24) +layout_mode = 2 +size_flags_vertical = 4 +tooltip_text = "TOOLTIP_SHOW_LINE_MATERIAL" +focus_mode = 0 +mouse_default_cursor_shape = 2 +theme_override_styles/focus = ExtResource("2_cxx1c") +theme_override_styles/disabled = ExtResource("2_cxx1c") +theme_override_styles/hover = ExtResource("2_cxx1c") +theme_override_styles/pressed = ExtResource("2_cxx1c") +theme_override_styles/normal = ExtResource("2_cxx1c") + +[node name="Sprite2D" type="Sprite2D" parent="VBoxContainer/LineMaterial/FillButton"] +material = SubResource("ShaderMaterial_wh8b0") +position = Vector2(12, 12) +scale = Vector2(0.03, 0.03) +texture = ExtResource("6_wg8l6") + +[node name="Panel" type="Panel" parent="VBoxContainer/LineMaterial/FillButton"] +layout_mode = 0 +offset_right = 24.0 +offset_bottom = 24.0 +mouse_filter = 2 +theme_override_styles/panel = SubResource("StyleBoxFlat_s8urq") + +[node name="ColorButton" type="Button" parent="VBoxContainer/LineMaterial"] +clip_contents = true +custom_minimum_size = Vector2(64, 32) +layout_mode = 2 +size_flags_horizontal = 3 +tooltip_text = "TOOLTIP_SELECT_LINE_MATERIAL" +focus_mode = 0 +mouse_default_cursor_shape = 2 +theme_override_styles/focus = SubResource("StyleBoxFlat_5xoro") +theme_override_styles/disabled = SubResource("StyleBoxEmpty_2syiy") +theme_override_styles/hover = SubResource("StyleBoxFlat_dkh1d") +theme_override_styles/pressed = SubResource("StyleBoxEmpty_4auj0") +theme_override_styles/normal = SubResource("StyleBoxEmpty_ycrbb") + +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/LineMaterial/ColorButton"] +layout_mode = 1 +anchors_preset = 4 +anchor_top = 0.5 +anchor_bottom = 0.5 +offset_top = -12.0 +offset_right = 146.0 +offset_bottom = 12.0 +grow_vertical = 2 + +[node name="Color" type="Panel" parent="VBoxContainer/LineMaterial/ColorButton/HBoxContainer"] +material = SubResource("ShaderMaterial_jic5n") +custom_minimum_size = Vector2(64, 0) +layout_mode = 2 +size_flags_horizontal = 0 +mouse_filter = 1 +theme_override_styles/panel = SubResource("StyleBoxFlat_tgaui") + +[node name="Label" type="Label" parent="VBoxContainer/LineMaterial/ColorButton/HBoxContainer"] +auto_translate_mode = 2 +layout_mode = 2 +theme_override_font_sizes/font_size = 12 +text = "Material 0" + +[node name="MaterialSelectPopup" parent="VBoxContainer/LineMaterial/ColorButton" instance=ExtResource("7_3ul3g")] +visible = false + +[node name="LineWidth" type="HBoxContainer" parent="VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 + +[node name="Label" type="Label" parent="VBoxContainer/LineWidth"] +custom_minimum_size = Vector2(42, 0) +layout_mode = 2 +text = "LAYER_SETTINGS_LINE_WIDTH" + +[node name="HSlider" type="HSlider" parent="VBoxContainer/LineWidth"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 4 +tooltip_text = "TOOLTIP_LINE_WIDTH" +max_value = 10.0 +step = 0.01 +allow_greater = true + +[node name="SpinBox" type="SpinBox" parent="VBoxContainer/LineWidth"] +layout_mode = 2 +tooltip_text = "TOOLTIP_LINE_WIDTH" +step = 0.01 +allow_greater = true +select_all_on_focus = true + +[node name="FillLayerMaterial" type="HBoxContainer" parent="VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_vertical = 4 +theme_override_constants/separation = 4 + +[node name="Label" type="Label" parent="VBoxContainer/FillLayerMaterial"] +custom_minimum_size = Vector2(42, 0) +layout_mode = 2 +text = "LAYER_SETTINGS_FILL" + +[node name="ColorButton" type="Button" parent="VBoxContainer/FillLayerMaterial"] +clip_contents = true +custom_minimum_size = Vector2(64, 32) +layout_mode = 2 +size_flags_horizontal = 3 +tooltip_text = "TOOLTIP_SELECT_FILL_LAYER_MATERIAL" +focus_mode = 0 +mouse_default_cursor_shape = 2 +theme_override_styles/focus = SubResource("StyleBoxFlat_5xoro") +theme_override_styles/disabled = SubResource("StyleBoxEmpty_2syiy") +theme_override_styles/hover = SubResource("StyleBoxFlat_dkh1d") +theme_override_styles/pressed = SubResource("StyleBoxEmpty_4auj0") +theme_override_styles/normal = SubResource("StyleBoxEmpty_ycrbb") + +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/FillLayerMaterial/ColorButton"] +layout_mode = 1 +anchors_preset = 4 +anchor_top = 0.5 +anchor_bottom = 0.5 +offset_top = -12.0 +offset_right = 146.0 +offset_bottom = 12.0 +grow_vertical = 2 + +[node name="Color" type="Panel" parent="VBoxContainer/FillLayerMaterial/ColorButton/HBoxContainer"] +material = SubResource("ShaderMaterial_jic5n") +custom_minimum_size = Vector2(64, 0) +layout_mode = 2 +size_flags_horizontal = 0 +mouse_filter = 1 +theme_override_styles/panel = SubResource("StyleBoxFlat_tgaui") + +[node name="Label" type="Label" parent="VBoxContainer/FillLayerMaterial/ColorButton/HBoxContainer"] +auto_translate_mode = 2 +layout_mode = 2 +theme_override_font_sizes/font_size = 12 +text = "Material 0" + +[node name="MaterialSelectPopup" parent="VBoxContainer/FillLayerMaterial/ColorButton" instance=ExtResource("7_3ul3g")] +visible = false + +[connection signal="button_down" from="VBoxContainer/FillMaterial/EmptyButton" to="." method="_on_empty_button_button_down"] +[connection signal="button_down" from="VBoxContainer/FillMaterial/FillButton" to="." method="_on_fill_button_button_down"] +[connection signal="button_down" from="VBoxContainer/FillMaterial/ColorButton" to="." method="_on_color_button_button_down"] +[connection signal="on_material_selected" from="VBoxContainer/FillMaterial/ColorButton/MaterialSelectPopup" to="." method="_on_material_list_popup_on_material_selected"] +[connection signal="button_down" from="VBoxContainer/LineMaterial/EmptyButton" to="." method="_on_line_empty_button_button_down"] +[connection signal="button_down" from="VBoxContainer/LineMaterial/FillButton" to="." method="_on_line_fill_button_button_down"] +[connection signal="button_down" from="VBoxContainer/LineMaterial/ColorButton" to="." method="_on_line_color_button_button_down"] +[connection signal="on_material_selected" from="VBoxContainer/LineMaterial/ColorButton/MaterialSelectPopup" to="." method="_on_line_material_list_popup_on_material_selected"] +[connection signal="value_changed" from="VBoxContainer/LineWidth/HSlider" to="." method="_on_h_slider_value_changed"] +[connection signal="value_changed" from="VBoxContainer/LineWidth/SpinBox" to="." method="_on_spin_box_value_changed"] +[connection signal="button_down" from="VBoxContainer/FillLayerMaterial/ColorButton" to="." method="_on_fill_layer_color_button_button_down"] +[connection signal="on_material_selected" from="VBoxContainer/FillLayerMaterial/ColorButton/MaterialSelectPopup" to="." method="_on_material_list_popup_on_fill_layer_material_selected"] diff --git a/godot/scenes/tab/material_list_tab.tscn b/godot/scenes/tab/material_list_tab.tscn new file mode 100644 index 0000000..d874a6d --- /dev/null +++ b/godot/scenes/tab/material_list_tab.tscn @@ -0,0 +1,143 @@ +[gd_scene load_steps=9 format=3 uid="uid://blv25euce0b64"] + +[ext_resource type="Script" path="res://src/tab/material_list_tab.gd" id="1_ftvdy"] +[ext_resource type="Shader" path="res://shaders/fill.gdshader" id="2_6lobi"] +[ext_resource type="Texture2D" uid="uid://6u0brruaecvq" path="res://icons/font_awesome/plus-solid.svg" id="3_s6swb"] +[ext_resource type="Texture2D" uid="uid://cfy4c56trjpcp" path="res://icons/font_awesome/trash-solid.svg" id="4_sf0jd"] +[ext_resource type="PackedScene" uid="uid://83wedgkgqa8o" path="res://scenes/node/list_drop_cursor.tscn" id="5_oph0f"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ieycl"] +content_margin_left = 2.0 +content_margin_top = 6.0 +content_margin_right = 2.0 +bg_color = Color(0.6, 0.6, 0.6, 0.392157) + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_2qbq5"] +shader = ExtResource("2_6lobi") +shader_parameter/fill_color = Color(1, 1, 1, 1) + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_f2bfi"] +content_margin_top = 2.0 +content_margin_bottom = 2.0 +bg_color = Color(0.6, 0.6, 0.6, 0) + +[node name="PaintMaterialListTab" type="PanelContainer"] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_ftvdy") + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +layout_mode = 2 +theme_override_constants/separation = 0 + +[node name="Buttons" type="PanelContainer" parent="VBoxContainer"] +layout_mode = 2 +theme_override_styles/panel = SubResource("StyleBoxFlat_ieycl") + +[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/Buttons"] +layout_mode = 2 + +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/Buttons/VBoxContainer"] +layout_mode = 2 + +[node name="AddButton" type="MenuButton" parent="VBoxContainer/Buttons/VBoxContainer/HBoxContainer"] +unique_name_in_owner = true +custom_minimum_size = Vector2(32, 32) +layout_mode = 2 +tooltip_text = "TOOLTIP_ADD_MATERIAL" +flat = false +expand_icon = true +item_count = 3 +popup/item_0/text = "MATERIAL_SOLID" +popup/item_1/text = "MATERIAL_LINEAR_GRADIENT" +popup/item_1/id = 1 +popup/item_2/text = "MATERIAL_RADIAL_GRADIENT" +popup/item_2/id = 2 + +[node name="Sprite2D" type="Sprite2D" parent="VBoxContainer/Buttons/VBoxContainer/HBoxContainer/AddButton"] +material = SubResource("ShaderMaterial_2qbq5") +position = Vector2(16, 16) +scale = Vector2(0.04, 0.04) +texture = ExtResource("3_s6swb") + +[node name="DeleteButton" type="Button" parent="VBoxContainer/Buttons/VBoxContainer/HBoxContainer"] +unique_name_in_owner = true +custom_minimum_size = Vector2(32, 32) +layout_mode = 2 +tooltip_text = "TOOLTIP_DELETE_MATERIAL" +focus_mode = 0 +expand_icon = true + +[node name="Sprite2D" type="Sprite2D" parent="VBoxContainer/Buttons/VBoxContainer/HBoxContainer/DeleteButton"] +material = SubResource("ShaderMaterial_2qbq5") +position = Vector2(16, 16) +scale = Vector2(0.03, 0.03) +texture = ExtResource("4_sf0jd") + +[node name="Popup" type="Popup" parent="VBoxContainer/Buttons/VBoxContainer/HBoxContainer/DeleteButton"] +title = "MATERIAL_CONFIRM_DELETE_TITLE" +initial_position = 2 +size = Vector2i(180, 100) + +[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/Buttons/VBoxContainer/HBoxContainer/DeleteButton/Popup"] +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -77.5 +offset_top = -29.0 +offset_right = 77.5 +offset_bottom = 29.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="Label" type="Label" parent="VBoxContainer/Buttons/VBoxContainer/HBoxContainer/DeleteButton/Popup/VBoxContainer"] +layout_mode = 2 +text = "MATERIAL_CONFIRM_DELETE_TEXT" + +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/Buttons/VBoxContainer/HBoxContainer/DeleteButton/Popup/VBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 4 + +[node name="OkButton" type="Button" parent="VBoxContainer/Buttons/VBoxContainer/HBoxContainer/DeleteButton/Popup/VBoxContainer/HBoxContainer"] +custom_minimum_size = Vector2(60, 0) +layout_mode = 2 +text = "MATERIAL_CONFIRM_DELETE_OK" + +[node name="CancelButton" type="Button" parent="VBoxContainer/Buttons/VBoxContainer/HBoxContainer/DeleteButton/Popup/VBoxContainer/HBoxContainer"] +layout_mode = 2 +text = "MATERIAL_CONFIRM_DELETE_CANCEL" + +[node name="HSeparator" type="HSeparator" parent="VBoxContainer/Buttons/VBoxContainer"] +layout_mode = 2 + +[node name="ScrollContainer" type="ScrollContainer" parent="VBoxContainer"] +layout_mode = 2 +size_flags_vertical = 3 +horizontal_scroll_mode = 0 + +[node name="PanelContainer" type="PanelContainer" parent="VBoxContainer/ScrollContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +mouse_filter = 1 +theme_override_styles/panel = SubResource("StyleBoxFlat_f2bfi") + +[node name="ListContainer" type="VBoxContainer" parent="VBoxContainer/ScrollContainer/PanelContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_constants/separation = 0 + +[node name="ListDropCursor" parent="VBoxContainer/ScrollContainer/PanelContainer/ListContainer" instance=ExtResource("5_oph0f")] +visible = false +layout_mode = 2 + +[connection signal="button_up" from="VBoxContainer/Buttons/VBoxContainer/HBoxContainer/DeleteButton" to="." method="_on_delete_button_button_up"] +[connection signal="button_up" from="VBoxContainer/Buttons/VBoxContainer/HBoxContainer/DeleteButton/Popup/VBoxContainer/HBoxContainer/OkButton" to="." method="_on_ok_button_button_up"] +[connection signal="button_up" from="VBoxContainer/Buttons/VBoxContainer/HBoxContainer/DeleteButton/Popup/VBoxContainer/HBoxContainer/CancelButton" to="." method="_on_cancel_button_button_up"] diff --git a/godot/shaders/composite/fill_composite.glsl b/godot/shaders/composite/fill_composite.glsl new file mode 100644 index 0000000..f9219fc --- /dev/null +++ b/godot/shaders/composite/fill_composite.glsl @@ -0,0 +1,75 @@ +#[compute] +#version 460 + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +layout(rgba8, set = 0, binding = 0) uniform image2D store_image; +layout(set = 1, binding = 0) uniform sampler2D gradientTexture; + +layout(push_constant, std430) uniform PushConstants { + int width; + int height; + int materialType; + int r8; + int g8; + int b8; + int a8; + int padding1; + vec2 pos0; + vec2 pos1; + vec2 pos2; + int padding2[2]; +} pushConstants; + +vec4 getColor() { + vec4 color; + if (pushConstants.materialType == 0) { + color.r = float(pushConstants.r8) / 255.0; + color.g = float(pushConstants.g8) / 255.0; + color.b = float(pushConstants.b8) / 255.0; + color.a = float(pushConstants.a8) / 255.0; + } else if (pushConstants.materialType == 1) { + vec2 v = pushConstants.pos1 - pushConstants.pos0; + vec2 uv = vec2(gl_GlobalInvocationID.xy); + float t = dot(uv - pushConstants.pos0, v) / dot(v, v); + color = texture(gradientTexture, vec2(t, 0.5)); + } else if (pushConstants.materialType == 2) { + vec2 a = pushConstants.pos1 - pushConstants.pos0; + vec2 b = pushConstants.pos2 - pushConstants.pos0; + float aLength = length(a); + float bLength = length(b); + vec2 aNorm = a / aLength; + vec2 bNorm = b / bLength; + if (aLength < 0.0001 || bLength < 0.0001) { + color = texture(gradientTexture, vec2(1.0, 0.5)); + } else { + vec2 uv = vec2(gl_GlobalInvocationID.xy); + vec2 p = uv - pushConstants.pos0; + vec2 pp = dot(p, aNorm) * aNorm + dot(p, bNorm) * bNorm / bLength * aLength; + float t = length(pp) / aLength; + color = texture(gradientTexture, vec2(t, 0.5)); + } + } else if (pushConstants.materialType == 3) { + vec2 checker_count = vec2(pushConstants.width, pushConstants.height) / 48.0; + vec2 uv = vec2(gl_GlobalInvocationID.xy) / vec2(pushConstants.width, pushConstants.height); + float checker = mod(dot(vec2(1.0), step(vec2(0.5), fract(uv * checker_count))), 2.0); + color.rgb = mix(vec3(1.0, 0.0, 1.0), vec3(0.0, 1.0, 1.0), checker); + color.a = 1.0; + } + return color; +} + +void main() { + ivec2 size = ivec2(pushConstants.width, pushConstants.height); + ivec2 iuv = ivec2(gl_GlobalInvocationID.xy); + + if (iuv.x >= size.x || iuv.y >= size.y) { + return; + } + + vec2 uv = (vec2(iuv) + vec2(0.5)) / vec2(size); + + vec4 color = getColor(); + + imageStore(store_image, iuv, color); +} diff --git a/godot/shaders/composite/fill_composite.glsl.import b/godot/shaders/composite/fill_composite.glsl.import new file mode 100644 index 0000000..72b7cfa --- /dev/null +++ b/godot/shaders/composite/fill_composite.glsl.import @@ -0,0 +1,14 @@ +[remap] + +importer="glsl" +type="RDShaderFile" +uid="uid://b3ju1dnvpywem" +path="res://.godot/imported/fill_composite.glsl-35dc6e7a02efd068412c7875e53fd001.res" + +[deps] + +source_file="res://shaders/composite/fill_composite.glsl" +dest_files=["res://.godot/imported/fill_composite.glsl-35dc6e7a02efd068412c7875e53fd001.res"] + +[params] + diff --git a/godot/shaders/composite/group_composite.glsl b/godot/shaders/composite/group_composite.glsl new file mode 100644 index 0000000..07253aa --- /dev/null +++ b/godot/shaders/composite/group_composite.glsl @@ -0,0 +1,94 @@ +#[compute] +#version 460 + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +layout(rgba8, set = 0, binding = 0) uniform image2D base_image; +layout(rgba8, set = 1, binding = 0) uniform readonly image2D foreground_image; +layout(rgba8, set = 2, binding = 0) uniform readonly image2D clipping_image; + +layout(push_constant, std430) uniform PushConstants { + int blendMode; + int clipping; + int width; + int height; + int alpha; + int clippingAlpha; + int mirror; +} pushConstants; + +void main() { + ivec2 size = ivec2(pushConstants.width, pushConstants.height); + ivec2 uv = ivec2(gl_GlobalInvocationID.xy); + + if (uv.x >= size.x || uv.y >= size.y) { + return; + } + + vec4 base = imageLoad(base_image, uv); + + if (pushConstants.mirror == 1) { + uv.x = size.x - uv.x - 1; + } + + vec4 foreground = imageLoad(foreground_image, uv); + vec4 clipping = imageLoad(clipping_image, uv); + + foreground.a *= float(pushConstants.alpha) / 100.0; + clipping.a *= float(pushConstants.clippingAlpha) / 100.0; + if (pushConstants.clipping == 1) { + foreground.a *= clipping.a; + } + + // Porter Dugg, Source Over + float f_b = 1 - foreground.a; + float f_f = 1; + + + vec4 color; + color.a = base.a * f_b + foreground.a * f_f; + + vec3 blendColor; + + switch (pushConstants.blendMode) { + case 0: // Normal + blendColor = foreground.rgb; + break; + case 1: // Add + blendColor =min(vec3(1), base.rgb + foreground.rgb); + break; + case 2: // Multiply + blendColor = base.rgb * foreground.rgb; + break; + case 3: // Screen + blendColor = 1 - (1 - base.rgb) * (1 - foreground.rgb); + break; + case 4: // Overlay + if (base.a < 0.5) { + blendColor = 2 * base.rgb * foreground.rgb; + } else { + blendColor = 1 - 2 * (1 - base.rgb) * (1 - foreground.rgb); + } + break; + } + + vec3 color_prime = blendColor * base.a + foreground.rgb * (1 - base.a); + color.rgb = (base.rgb * f_b * base.a + color_prime * f_f * foreground.a) / color.a; + + if (pushConstants.clipping == 0) { + if (color.a == 0.0) { + color.rgb = vec3(0.0); + } + } else { + if (color.a == 0.0) { + return; + } + } + + + if (pushConstants.mirror == 1) { + uv.x = size.x - uv.x - 1; + } + + imageStore(base_image, uv, color); +} diff --git a/godot/shaders/composite/group_composite.glsl.import b/godot/shaders/composite/group_composite.glsl.import new file mode 100644 index 0000000..395f5ec --- /dev/null +++ b/godot/shaders/composite/group_composite.glsl.import @@ -0,0 +1,14 @@ +[remap] + +importer="glsl" +type="RDShaderFile" +uid="uid://b7nioxxufeqwh" +path="res://.godot/imported/group_composite.glsl-9304158d0cfaee1e18c82cbcc4938014.res" + +[deps] + +source_file="res://shaders/composite/group_composite.glsl" +dest_files=["res://.godot/imported/group_composite.glsl-9304158d0cfaee1e18c82cbcc4938014.res"] + +[params] + diff --git a/godot/shaders/composite/init_jfa_line.glsl b/godot/shaders/composite/init_jfa_line.glsl new file mode 100644 index 0000000..b8b4156 --- /dev/null +++ b/godot/shaders/composite/init_jfa_line.glsl @@ -0,0 +1,34 @@ +#[compute] +#version 460 + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +layout(rgba32f, set = 0, binding = 0) uniform image2D store_image; +layout(rgba8, set = 1, binding = 0) uniform readonly image2D viewport_image; + + +layout(push_constant, std430) uniform PushConstants { + int width; + int height; + int padding0; + int padding1; +} pushConstants; + +void main() { + ivec2 size = ivec2(pushConstants.width, pushConstants.height); + ivec2 uv = ivec2(gl_GlobalInvocationID.xy); + + if (uv.x >= size.x || uv.y >= size.y) { + return; + } + + vec4 viewport = imageLoad(viewport_image, uv); + + vec4 init; + if (viewport.r == 1.0) { + init = vec4(0.0, -1.0, 1.0, 0.0); + } else { + init = vec4(-1.0, 0.0, 0.0, 1.0); + } + imageStore(store_image, uv, init); +} diff --git a/godot/shaders/composite/init_jfa_line.glsl.import b/godot/shaders/composite/init_jfa_line.glsl.import new file mode 100644 index 0000000..27a78e5 --- /dev/null +++ b/godot/shaders/composite/init_jfa_line.glsl.import @@ -0,0 +1,14 @@ +[remap] + +importer="glsl" +type="RDShaderFile" +uid="uid://cmq2gvcm4lhss" +path="res://.godot/imported/init_jfa_line.glsl-f5884ea84b633ab94427f53e5c4a7bcd.res" + +[deps] + +source_file="res://shaders/composite/init_jfa_line.glsl" +dest_files=["res://.godot/imported/init_jfa_line.glsl-f5884ea84b633ab94427f53e5c4a7bcd.res"] + +[params] + diff --git a/godot/shaders/composite/jfa_line.glsl b/godot/shaders/composite/jfa_line.glsl new file mode 100644 index 0000000..e035a3b --- /dev/null +++ b/godot/shaders/composite/jfa_line.glsl @@ -0,0 +1,112 @@ +#[compute] +#version 460 + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +layout(rgba32f, set = 0, binding = 0) uniform image2D store_image; +layout(rgba32f, set = 1, binding = 0) uniform readonly image2D read_image; + +layout(push_constant, std430) uniform PushConstants { + int width; + int height; + int space; + int padding1; +} pushConstants; + +const float ROOT2 = 1.41421356237; + +void main() { + ivec2 size = ivec2(pushConstants.width, pushConstants.height); + ivec2 uv = ivec2(gl_GlobalInvocationID.xy); + + if (uv.x >= size.x || uv.y >= size.y) { + return; + } + + float space = float(pushConstants.space); + float spaceRoot2 = space * ROOT2; + + vec4 valueCenter = imageLoad(read_image, uv); + + ivec2 topLeftUV = ivec2(max(uv.x - space, 0), max(uv.y - space, 0)); + vec4 valueTopLeft = imageLoad(read_image, topLeftUV); + + ivec2 topUV = ivec2(uv.x, max(uv.y - space, 0)); + vec4 valueTop = imageLoad(read_image, topUV); + + ivec2 topRightUV = ivec2(min(uv.x + space, size.x - 1), max(uv.y - space, 0)); + vec4 valueTopRight = imageLoad(read_image, topRightUV); + + ivec2 leftUV = ivec2(max(uv.x - space, 0), uv.y); + vec4 valueLeft = imageLoad(read_image, leftUV); + + ivec2 rightUV = ivec2(min(uv.x + space, size.x - 1), uv.y); + vec4 valueRight = imageLoad(read_image, rightUV); + + ivec2 bottomLeftUV = ivec2(max(uv.x - space, 0), min(uv.y + space, size.y - 1)); + vec4 valueBottomLeft = imageLoad(read_image, bottomLeftUV); + + ivec2 bottomUV = ivec2(uv.x, min(uv.y + space, size.y - 1)); + vec4 valueBottom = imageLoad(read_image, bottomUV); + + ivec2 bottomRightUV = ivec2(min(uv.x + space, size.x - 1), min(uv.y + space, size.y - 1)); + vec4 valueBottomRight = imageLoad(read_image, bottomRightUV); + + float minR = valueCenter.r; + if (valueTopLeft.r != -1.0 && (minR == -1.0 || valueTopLeft.r + spaceRoot2 < minR)) { + minR = valueTopLeft.r + spaceRoot2; + } + if (valueTop.r != -1.0 && (minR == -1.0 || valueTop.r + space < minR)) { + minR = valueTop.r + space; + } + if (valueTopRight.r != -1.0 && (minR == -1.0 || valueTopRight.r + spaceRoot2 < minR)) { + minR = valueTopRight.r + spaceRoot2; + } + if (valueLeft.r != -1.0 && (minR == -1.0 || valueLeft.r + space < minR)) { + minR = valueLeft.r + space; + } + if (valueRight.r != -1.0 && (minR == -1.0 || valueRight.r + space < minR)) { + minR = valueRight.r + space; + } + if (valueBottomLeft.r != -1.0 && (minR == -1.0 || valueBottomLeft.r + spaceRoot2 < minR)) { + minR = valueBottomLeft.r + spaceRoot2; + } + if (valueBottom.r != -1.0 && (minR == -1.0 || valueBottom.r + space < minR)) { + minR = valueBottom.r + space; + } + if (valueBottomRight.r != -1.0 && (minR == -1.0 || valueBottomRight.r + spaceRoot2 < minR)) { + minR = valueBottomRight.r + spaceRoot2; + } + + float minG = valueCenter.g; + if (valueTopLeft.g != -1.0 && (minG == -1.0 || valueTopLeft.g + spaceRoot2 < minG)) { + minG = valueTopLeft.g + spaceRoot2; + } + if (valueTop.g != -1.0 && (minG == -1.0 || valueTop.g + space < minG)) { + minG = valueTop.g + space; + } + if (valueTopRight.g != -1.0 && (minG == -1.0 || valueTopRight.g + spaceRoot2 < minG)) { + minG = valueTopRight.g + spaceRoot2; + } + if (valueLeft.g != -1.0 && (minG == -1.0 || valueLeft.g + space < minG)) { + minG = valueLeft.g + space; + } + if (valueRight.g != -1.0 && (minG == -1.0 || valueRight.g + space < minG)) { + minG = valueRight.g + space; + } + if (valueBottomLeft.g != -1.0 && (minG == -1.0 || valueBottomLeft.g + spaceRoot2 < minG)) { + minG = valueBottomLeft.g + spaceRoot2; + } + if (valueBottom.g != -1.0 && (minG == -1.0 || valueBottom.g + space < minG)) { + minG = valueBottom.g + space; + } + if (valueBottomRight.g != -1.0 && (minG == -1.0 || valueBottomRight.g + spaceRoot2 < minG)) { + minG = valueBottomRight.g + spaceRoot2; + } + + float b = valueCenter.b; + float a = valueCenter.a; + + vec4 value = vec4(minR, minG, b, a); + imageStore(store_image, uv, value); +} diff --git a/godot/shaders/composite/jfa_line.glsl.import b/godot/shaders/composite/jfa_line.glsl.import new file mode 100644 index 0000000..fcded4c --- /dev/null +++ b/godot/shaders/composite/jfa_line.glsl.import @@ -0,0 +1,14 @@ +[remap] + +importer="glsl" +type="RDShaderFile" +uid="uid://b4scg4ifd6jt1" +path="res://.godot/imported/jfa_line.glsl-105bdb2048aadcc72bbd2e9b55d4dddd.res" + +[deps] + +source_file="res://shaders/composite/jfa_line.glsl" +dest_files=["res://.godot/imported/jfa_line.glsl-105bdb2048aadcc72bbd2e9b55d4dddd.res"] + +[params] + diff --git a/godot/shaders/composite/mipmap_generate.glsl b/godot/shaders/composite/mipmap_generate.glsl new file mode 100644 index 0000000..e8c41b9 --- /dev/null +++ b/godot/shaders/composite/mipmap_generate.glsl @@ -0,0 +1,28 @@ +#[compute] +#version 460 + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +layout(rgba8, set = 0, binding = 0) uniform image2D store_image; +layout(set = 1, binding = 0) uniform sampler2D input_image; + +layout(push_constant, std430) uniform PushConstants { + int width; + int height; + int padding0; + int padding1; +} pushConstants; + +void main() { + ivec2 size = ivec2(pushConstants.width, pushConstants.height); + ivec2 iuv = ivec2(gl_GlobalInvocationID.xy); + + if (iuv.x >= size.x || iuv.y >= size.y) { + return; + } + + vec2 uv = (vec2(iuv) + vec2(0.5)) / vec2(size); + + vec4 color = texture(input_image, uv); + imageStore(store_image, iuv, color); +} diff --git a/godot/shaders/composite/mipmap_generate.glsl.import b/godot/shaders/composite/mipmap_generate.glsl.import new file mode 100644 index 0000000..5e5b86e --- /dev/null +++ b/godot/shaders/composite/mipmap_generate.glsl.import @@ -0,0 +1,14 @@ +[remap] + +importer="glsl" +type="RDShaderFile" +uid="uid://mte48di0snd4" +path="res://.godot/imported/mipmap_generate.glsl-47a90a1d5c7d46976241bcfbdd9372c4.res" + +[deps] + +source_file="res://shaders/composite/mipmap_generate.glsl" +dest_files=["res://.godot/imported/mipmap_generate.glsl-47a90a1d5c7d46976241bcfbdd9372c4.res"] + +[params] + diff --git a/godot/shaders/composite/path_composite.glsl b/godot/shaders/composite/path_composite.glsl new file mode 100644 index 0000000..36da405 --- /dev/null +++ b/godot/shaders/composite/path_composite.glsl @@ -0,0 +1,260 @@ +#[compute] +#version 460 + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +layout(rgba8, set = 0, binding = 0) uniform image2D base_image; +layout(set = 1, binding = 0) uniform sampler2D viewport_image; +layout(set = 2, binding = 0) uniform sampler2D gradientTexture; + +const float FIXED_THRESHOLD = 0.0833; +const float RELATIVE_THRESHOLD = 0.166; +const float SUBPIXEL_BLENDING = 0.75; +const float EDGE_STEP_SIZES_ARRAY[10] = float[]( + 1.0, 1.0, 1.0, 1.0, 1.5, 2.0, 2.0, 2.0, 2.0, 4.0 +); +const float LAST_EDGE_STEP_GUESS = 8.0; + +layout(push_constant, std430) uniform PushConstants { + int width; + int height; + int materialType; + int r8; + int g8; + int b8; + int a8; + int padding1; + vec2 pos0; + vec2 pos1; + vec2 pos2; + int padding2[2]; +} pushConstants; + +float getMaskOffset(vec2 uv, float uOffset, float vOffset) { + uv += vec2(uOffset + 0.5, vOffset + 0.5) / vec2(pushConstants.width, pushConstants.height); + return textureLod(viewport_image, uv, 0.0).r; +} + +struct MaskNeighbor { + float center; + float topLeft; + float top; + float topRight; + float left; + float right; + float bottomLeft; + float bottom; + float bottomRight; + float highest; + float lowest; + float range; +}; + +MaskNeighbor getMaskNeighbor(vec2 uv) { + MaskNeighbor neighbor; + neighbor.center = getMaskOffset(uv, 0.0, 0.0); + neighbor.topLeft = getMaskOffset(uv, -1.0, -1.0); + neighbor.top = getMaskOffset(uv, 0.0, -1.0); + neighbor.topRight = getMaskOffset(uv, 1.0, -1.0); + neighbor.left = getMaskOffset(uv, -1.0, 0.0); + neighbor.right = getMaskOffset(uv, 1.0, 0.0); + neighbor.bottomLeft = getMaskOffset(uv, -1.0, 1.0); + neighbor.bottom = getMaskOffset(uv, 0.0, 1.0); + neighbor.bottomRight = getMaskOffset(uv, 1.0, 1.0); + neighbor.highest = max(max(max(max(neighbor.center, neighbor.top), neighbor.left), neighbor.right), neighbor.bottom); + neighbor.lowest = min(min(min(min(neighbor.center, neighbor.top), neighbor.left), neighbor.right), neighbor.bottom); + neighbor.range = neighbor.highest - neighbor.lowest; + return neighbor; +} + +bool isHorizontal(MaskNeighbor neighbor) { + float horizontal = 2.0 * abs(neighbor.top + neighbor.bottom - 2.0 * neighbor.center) + abs(neighbor.topLeft+ neighbor.bottomLeft - 2.0 * neighbor.left) + abs(neighbor.topRight + neighbor.bottomRight - 2.0 * neighbor.right); + float vertical = 2.0 * abs(neighbor.left + neighbor.right - 2.0 * neighbor.center) + abs(neighbor.topLeft + neighbor.topRight - 2.0 * neighbor.top) + abs(neighbor.bottomLeft + neighbor.bottomRight - 2.0 * neighbor.bottom); + return horizontal >= vertical; +} + +struct FxaaEdge { + bool isHorizontal; + float pixelStep; + float maskGradient; + float otherMask; +}; + +FxaaEdge getFxaaEdge(MaskNeighbor neighbor) { + FxaaEdge edge; + edge.isHorizontal = isHorizontal(neighbor); + float maskP, maskN; + if (edge.isHorizontal) { + edge.pixelStep = 1.0 / float(pushConstants.height); + maskP = neighbor.top; + maskN = neighbor.bottom; + } else { + edge.pixelStep = 1.0 / float(pushConstants.width); + maskP = neighbor.left; + maskN = neighbor.right; + } + float gradientP = abs(maskP - neighbor.center); + float gradientN = abs(maskN - neighbor.center); + if (gradientP < gradientN) { + edge.pixelStep = -edge.pixelStep; + edge.maskGradient = gradientN; + edge.otherMask = maskN; + } else { + edge.maskGradient = gradientP; + edge.otherMask = maskP; + } + return edge; +} + +bool canSkipFxaa(MaskNeighbor neighbor) { + return neighbor.range < max(FIXED_THRESHOLD, RELATIVE_THRESHOLD * neighbor.highest); +} + +float getSubpixelBlendFactor(MaskNeighbor neighbor) { + float filterValue = 2.0 * (neighbor.top + neighbor.bottom + neighbor.left + neighbor.right); + filterValue += neighbor.topLeft + neighbor.topRight + neighbor.bottomLeft + neighbor.bottomRight; + filterValue *= 1.0 / 12.0; + filterValue = abs(filterValue - neighbor.center); + filterValue = clamp(filterValue / neighbor.range, 0.0, 1.0); + filterValue = smoothstep(0.0, 1.0, filterValue); + return filterValue * filterValue * SUBPIXEL_BLENDING; +} + +float getEdgeBlendFactor(MaskNeighbor neighbor, FxaaEdge edge, vec2 uv) { + vec2 edgeUV = uv; + vec2 uvStep = vec2(0.0); + if (edge.isHorizontal) { + uvStep.y += 0.5 * edge.pixelStep; + uvStep.x = 1.0 / float(pushConstants.width); + } else { + uvStep.x += 0.5 * edge.pixelStep; + uvStep.y = 1.0 / float(pushConstants.height); + } + + float edgeMask = 0.5 * (neighbor.center + edge.otherMask); + float gradientThreshold = 0.25 * edge.maskGradient; + + vec2 uvP = edgeUV + uvStep; + float maskDeltaP = getMaskOffset(uvP, 0.0, 0.0) - edgeMask; + bool atEndP = abs(maskDeltaP) >= gradientThreshold; + + int i; + for (i = 0; i < 10 && !atEndP; i++) { + uvP += uvStep * EDGE_STEP_SIZES_ARRAY[i]; + maskDeltaP = getMaskOffset(uvP, 0.0, 0.0) - edgeMask; + atEndP = abs(maskDeltaP) >= gradientThreshold; + } + if (!atEndP) { + uvP = edgeUV + uvStep * LAST_EDGE_STEP_GUESS; + } + + vec2 uvN = edgeUV - uvStep; + float maskDeltaN = getMaskOffset(uvN, 0.0, 0.0) - edgeMask; + bool atEndN = abs(maskDeltaN) >= gradientThreshold; + + for (i = 0; i < 10 && !atEndN; i++) { + uvN -= uvStep * EDGE_STEP_SIZES_ARRAY[i]; + maskDeltaN = getMaskOffset(uvN, 0.0, 0.0) - edgeMask; + atEndN = abs(maskDeltaN) >= gradientThreshold; + } + if (!atEndN) { + uvN = edgeUV - uvStep * LAST_EDGE_STEP_GUESS; + } + + float distanceToEndP, distanceToEndN; + if (edge.isHorizontal) { + distanceToEndP = uvP.x - uv.x; + distanceToEndN = uv.x - uvN.x; + } else { + distanceToEndP = uvP.y - uv.y; + distanceToEndN = uv.y - uvN.y; + } + + float distanceToNearestEnd; + bool deltaSign; + if (distanceToEndP <= distanceToEndN) { + distanceToNearestEnd = distanceToEndP; + deltaSign = maskDeltaP >= 0; + } else { + distanceToNearestEnd = distanceToEndN; + deltaSign = maskDeltaN >= 0; + } + + if (deltaSign == (neighbor.center - edgeMask >= 0)) { + return 0.0; + } else { + return 0.5 - distanceToNearestEnd / (distanceToEndP + distanceToEndN); + } +} + +float getMask(vec2 uv) { + MaskNeighbor neighbor = getMaskNeighbor(uv); + if (canSkipFxaa(neighbor)) { + return neighbor.center; + } + + FxaaEdge edge = getFxaaEdge(neighbor); + float blendFactor = max(getSubpixelBlendFactor(neighbor), getEdgeBlendFactor(neighbor, edge, uv)); + if (edge.isHorizontal) { + return getMaskOffset(uv, 0.0, blendFactor); + } else { + return getMaskOffset(uv, blendFactor, 0.0); + } +} + +vec4 getColor() { + vec4 color; + if (pushConstants.materialType == 0) { + color.r = float(pushConstants.r8) / 255.0; + color.g = float(pushConstants.g8) / 255.0; + color.b = float(pushConstants.b8) / 255.0; + color.a = float(pushConstants.a8) / 255.0; + } else if (pushConstants.materialType == 1) { + vec2 v = (pushConstants.pos1 - pushConstants.pos0); + vec2 uv = vec2(gl_GlobalInvocationID.xy) / 2.0; + float t = dot(uv - pushConstants.pos0, v) / dot(v, v); + color = texture(gradientTexture, vec2(t, 0.5)); + } else if (pushConstants.materialType == 2) { + vec2 a = pushConstants.pos1 - pushConstants.pos0; + vec2 b = pushConstants.pos2 - pushConstants.pos0; + float aLength = length(a); + float bLength = length(b); + vec2 aNorm = a / aLength; + vec2 bNorm = b / bLength; + if (aLength < 0.0001 || bLength < 0.0001) { + color = texture(gradientTexture, vec2(1.0, 0.5)); + color = vec4(1.0, 0.0, 1.0, 1.0); + } else { + vec2 uv = vec2(gl_GlobalInvocationID.xy) / 2.0; + vec2 p = uv - pushConstants.pos0; + vec2 pp = dot(p, aNorm) * aNorm + dot(p, bNorm) * bNorm / bLength * aLength; + float t = length(pp) / aLength; + color = texture(gradientTexture, vec2(t, 0.5)); + } + } else if (pushConstants.materialType == 3) { + vec2 checker_count = vec2(pushConstants.width, pushConstants.height) / 96.0; + vec2 uv = vec2(gl_GlobalInvocationID.xy) / vec2(pushConstants.width, pushConstants.height); + float checker = mod(dot(vec2(1.0), step(vec2(0.5), fract(uv * checker_count))), 2.0); + color.rgb = mix(vec3(1.0, 0.0, 1.0), vec3(0.0, 1.0, 1.0), checker); + color.a = 1.0; + } + return color; +} + +void main() { + ivec2 size = ivec2(pushConstants.width, pushConstants.height); + ivec2 iuv = ivec2(gl_GlobalInvocationID.xy); + + if (iuv.x >= size.x || iuv.y >= size.y) { + return; + } + + vec2 uv = (vec2(iuv) + vec2(0.5)) / vec2(size); + + vec4 color = getColor(); + + float mask = getMask(uv); + color.a *= mask; + + imageStore(base_image, iuv, color); +} diff --git a/godot/shaders/composite/path_composite.glsl.import b/godot/shaders/composite/path_composite.glsl.import new file mode 100644 index 0000000..36706be --- /dev/null +++ b/godot/shaders/composite/path_composite.glsl.import @@ -0,0 +1,14 @@ +[remap] + +importer="glsl" +type="RDShaderFile" +uid="uid://cio6xryo0ejm" +path="res://.godot/imported/path_composite.glsl-57a968e952803a07816326f075cb0e81.res" + +[deps] + +source_file="res://shaders/composite/path_composite.glsl" +dest_files=["res://.godot/imported/path_composite.glsl-57a968e952803a07816326f075cb0e81.res"] + +[params] + diff --git a/godot/shaders/composite/path_composite_jfa_line.glsl b/godot/shaders/composite/path_composite_jfa_line.glsl new file mode 100644 index 0000000..7ceb034 --- /dev/null +++ b/godot/shaders/composite/path_composite_jfa_line.glsl @@ -0,0 +1,472 @@ +#[compute] +#version 460 + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +layout(rgba8, set = 0, binding = 0) uniform image2D store_image; +layout(rgba32f, set = 1, binding = 0) uniform readonly image2D jfa_image; +layout(set = 2, binding = 0) uniform sampler2D fillGradientTexture; +layout(set = 3, binding = 0) uniform sampler2D lineGradientTexture; + +const float FIXED_THRESHOLD = 0.0833; +const float RELATIVE_THRESHOLD = 0.166; +const float SUBPIXEL_BLENDING = 0.75; +const float EDGE_STEP_SIZES_ARRAY[10] = float[]( + 1.0, 1.0, 1.0, 1.0, 1.5, 2.0, 2.0, 2.0, 2.0, 4.0 +); +const float LAST_EDGE_STEP_GUESS = 8.0; + + +layout(push_constant, std430) uniform PushConstants { + int width; + int height; + int fillMaterialType; + int fillR8; + int fillG8; + int fillB8; + int fillA8; + int padding0; + vec2 fillPos0; + vec2 fillPos1; + vec2 fillPos2; + int lineMaterialType; + int lineR8; + int lineG8; + int lineB8; + int lineA8; + int padding1; + vec2 linePos0; + vec2 linePos1; + vec2 linePos2; + float lineWidth; + int padding2; +} pushConstants; + + +float getFillMaskValue(ivec2 iuv) { + vec4 jfa = imageLoad(jfa_image, iuv); + + if (jfa.b == 1.0) { + return 1.0; + } else { + return 0.0; + } +} + +float getFillMaskValueOffset(vec2 uv) { + ivec2 iuv = ivec2(uv * vec2(pushConstants.width, pushConstants.height)); + float maskTopLeft = getFillMaskValue(iuv); + float maskTopRight = getFillMaskValue(iuv + ivec2(1, 0)); + float maskBottomLeft = getFillMaskValue(iuv + ivec2(0, 1)); + float maskBottomRight = getFillMaskValue(iuv + ivec2(1, 1)); + uv += vec2(0.5) / vec2(pushConstants.width, pushConstants.height); + vec2 blendFactor = fract(uv * vec2(pushConstants.width, pushConstants.height)); + return mix( + mix(maskTopLeft, maskTopRight, blendFactor.x), + mix(maskBottomLeft, maskBottomRight, blendFactor.x), + blendFactor.y + ); +} + +float getLineMaskValue(ivec2 iuv) { + vec4 jfa = imageLoad(jfa_image, iuv); + + if ( + (jfa.b == 0.0 && jfa.r < pushConstants.lineWidth && jfa.r != -1) || + (jfa.a == 0.0 && jfa.g < pushConstants.lineWidth && jfa.g != -1) + ) { + return 1.0; + } else { + return 0.0; + } +} + +float getLineMaskValueOffset(vec2 uv) { + ivec2 iuv = ivec2(uv * vec2(pushConstants.width, pushConstants.height)); + float maskTopLeft = getLineMaskValue(iuv); + float maskTopRight = getLineMaskValue(iuv + ivec2(1, 0)); + float maskBottomLeft = getLineMaskValue(iuv + ivec2(0, 1)); + float maskBottomRight = getLineMaskValue(iuv + ivec2(1, 1)); + uv += vec2(0.5) / vec2(pushConstants.width, pushConstants.height); + vec2 blendFactor = fract(uv * vec2(pushConstants.width, pushConstants.height)); + return mix( + mix(maskTopLeft, maskTopRight, blendFactor.x), + mix(maskBottomLeft, maskBottomRight, blendFactor.x), + blendFactor.y + ); +} + +struct MaskNeighbor { + float center; + float topLeft; + float top; + float topRight; + float left; + float right; + float bottomLeft; + float bottom; + float bottomRight; + float highest; + float lowest; + float range; +}; + +MaskNeighbor getFillMaskNeighbor(ivec2 iuv) { + MaskNeighbor neighbor; + neighbor.center = getFillMaskValue(iuv); + neighbor.topLeft = getFillMaskValue(iuv + ivec2(-1, -1)); + neighbor.top = getFillMaskValue(iuv + ivec2(0, -1)); + neighbor.topRight = getFillMaskValue(iuv + ivec2(1, -1)); + neighbor.left = getFillMaskValue(iuv + ivec2(-1, 0)); + neighbor.right = getFillMaskValue(iuv + ivec2(1, 0)); + neighbor.bottomLeft = getFillMaskValue(iuv + ivec2(-1, 1)); + neighbor.bottom = getFillMaskValue(iuv + ivec2(0, 1)); + neighbor.bottomRight = getFillMaskValue(iuv + ivec2(1, 1)); + neighbor.highest = max(max(max(max(neighbor.center, neighbor.top), neighbor.left), neighbor.right), neighbor.bottom); + neighbor.lowest = min(min(min(min(neighbor.center, neighbor.top), neighbor.left), neighbor.right), neighbor.bottom); + neighbor.range = neighbor.highest - neighbor.lowest; + return neighbor; +} + +MaskNeighbor getLineMaskNeighbor(ivec2 iuv) { + MaskNeighbor neighbor; + neighbor.center = getLineMaskValue(iuv); + neighbor.topLeft = getLineMaskValue(iuv + ivec2(-1, -1)); + neighbor.top = getLineMaskValue(iuv + ivec2(0, -1)); + neighbor.topRight = getLineMaskValue(iuv + ivec2(1, -1)); + neighbor.left = getLineMaskValue(iuv + ivec2(-1, 0)); + neighbor.right = getLineMaskValue(iuv + ivec2(1, 0)); + neighbor.bottomLeft = getLineMaskValue(iuv + ivec2(-1, 1)); + neighbor.bottom = getLineMaskValue(iuv + ivec2(0, 1)); + neighbor.bottomRight = getLineMaskValue(iuv + ivec2(1, 1)); + neighbor.highest = max(max(max(max(neighbor.center, neighbor.top), neighbor.left), neighbor.right), neighbor.bottom); + neighbor.lowest = min(min(min(min(neighbor.center, neighbor.top), neighbor.left), neighbor.right), neighbor.bottom); + neighbor.range = neighbor.highest - neighbor.lowest; + return neighbor; +} + + +bool isHorizontal(MaskNeighbor neighbor) { + float horizontal = 2.0 * abs(neighbor.top + neighbor.bottom - 2.0 * neighbor.center) + abs(neighbor.topLeft+ neighbor.bottomLeft - 2.0 * neighbor.left) + abs(neighbor.topRight + neighbor.bottomRight - 2.0 * neighbor.right); + float vertical = 2.0 * abs(neighbor.left + neighbor.right - 2.0 * neighbor.center) + abs(neighbor.topLeft + neighbor.topRight - 2.0 * neighbor.top) + abs(neighbor.bottomLeft + neighbor.bottomRight - 2.0 * neighbor.bottom); + return horizontal >= vertical; +} + +struct FxaaEdge { + bool isHorizontal; + float pixelStep; + float maskGradient; + float otherMask; +}; + +FxaaEdge getFxaaEdge(MaskNeighbor neighbor) { + FxaaEdge edge; + edge.isHorizontal = isHorizontal(neighbor); + float maskP, maskN; + if (edge.isHorizontal) { + edge.pixelStep = 1.0 / float(pushConstants.height); + maskP = neighbor.top; + maskN = neighbor.bottom; + } else { + edge.pixelStep = 1.0 / float(pushConstants.width); + maskP = neighbor.left; + maskN = neighbor.right; + } + float gradientP = abs(maskP - neighbor.center); + float gradientN = abs(maskN - neighbor.center); + if (gradientP < gradientN) { + edge.pixelStep = -edge.pixelStep; + edge.maskGradient = gradientN; + edge.otherMask = maskN; + } else { + edge.maskGradient = gradientP; + edge.otherMask = maskP; + } + return edge; +} + +bool canSkipFxaa(MaskNeighbor neighbor) { + return neighbor.range < max(FIXED_THRESHOLD, RELATIVE_THRESHOLD * neighbor.highest); +} + +float getSubpixelBlendFactor(MaskNeighbor neighbor) { + float filterValue = 2.0 * (neighbor.top + neighbor.bottom + neighbor.left + neighbor.right); + filterValue += neighbor.topLeft + neighbor.topRight + neighbor.bottomLeft + neighbor.bottomRight; + filterValue *= 1.0 / 12.0; + filterValue = abs(filterValue - neighbor.center); + filterValue = clamp(filterValue / neighbor.range, 0.0, 1.0); + filterValue = smoothstep(0.0, 1.0, filterValue); + return filterValue * filterValue * SUBPIXEL_BLENDING; +} + +float getFillEdgeBlendFactor(MaskNeighbor neighbor, FxaaEdge edge, vec2 uv) { + vec2 edgeUV = uv; + vec2 uvStep = vec2(0.0); + if (edge.isHorizontal) { + uvStep.y += 0.5 * edge.pixelStep; + uvStep.x = 1.0 / float(pushConstants.width); + } else { + uvStep.x += 0.5 * edge.pixelStep; + uvStep.y = 1.0 / float(pushConstants.height); + } + + float edgeMask = 0.5 * (neighbor.center + edge.otherMask); + float gradientThreshold = 0.25 * edge.maskGradient; + + vec2 uvP = edgeUV + uvStep; + float maskDeltaP = getFillMaskValueOffset(uvP) - edgeMask; + bool atEndP = abs(maskDeltaP) >= gradientThreshold; + + int i; + for (i = 0; i < 10 && !atEndP; i++) { + uvP += uvStep * EDGE_STEP_SIZES_ARRAY[i]; + maskDeltaP = getFillMaskValueOffset(uvP) - edgeMask; + atEndP = abs(maskDeltaP) >= gradientThreshold; + } + if (!atEndP) { + uvP = edgeUV + uvStep * LAST_EDGE_STEP_GUESS; + } + + vec2 uvN = edgeUV - uvStep; + float maskDeltaN = getFillMaskValueOffset(uvN) - edgeMask; + bool atEndN = abs(maskDeltaN) >= gradientThreshold; + + for (i = 0; i < 10 && !atEndN; i++) { + uvN -= uvStep * EDGE_STEP_SIZES_ARRAY[i]; + maskDeltaN = getFillMaskValueOffset(uvN) - edgeMask; + atEndN = abs(maskDeltaN) >= gradientThreshold; + } + if (!atEndN) { + uvN = edgeUV - uvStep * LAST_EDGE_STEP_GUESS; + } + + float distanceToEndP, distanceToEndN; + if (edge.isHorizontal) { + distanceToEndP = uvP.x - uv.x; + distanceToEndN = uv.x - uvN.x; + } else { + distanceToEndP = uvP.y - uv.y; + distanceToEndN = uv.y - uvN.y; + } + + float distanceToNearestEnd; + bool deltaSign; + if (distanceToEndP <= distanceToEndN) { + distanceToNearestEnd = distanceToEndP; + deltaSign = maskDeltaP >= 0; + } else { + distanceToNearestEnd = distanceToEndN; + deltaSign = maskDeltaN >= 0; + } + + if (deltaSign == (neighbor.center - edgeMask >= 0)) { + return 0.0; + } else { + return 0.5 - distanceToNearestEnd / (distanceToEndP + distanceToEndN); + } +} + +float getLineEdgeBlendFactor(MaskNeighbor neighbor, FxaaEdge edge, vec2 uv) { + vec2 edgeUV = uv; + vec2 uvStep = vec2(0.0); + if (edge.isHorizontal) { + uvStep.y += 0.5 * edge.pixelStep; + uvStep.x = 1.0 / float(pushConstants.width); + } else { + uvStep.x += 0.5 * edge.pixelStep; + uvStep.y = 1.0 / float(pushConstants.height); + } + + float edgeMask = 0.5 * (neighbor.center + edge.otherMask); + float gradientThreshold = 0.25 * edge.maskGradient; + + vec2 uvP = edgeUV + uvStep; + float maskDeltaP = getLineMaskValueOffset(uvP) - edgeMask; + bool atEndP = abs(maskDeltaP) >= gradientThreshold; + + int i; + for (i = 0; i < 10 && !atEndP; i++) { + uvP += uvStep * EDGE_STEP_SIZES_ARRAY[i]; + maskDeltaP = getLineMaskValueOffset(uvP) - edgeMask; + atEndP = abs(maskDeltaP) >= gradientThreshold; + } + if (!atEndP) { + uvP = edgeUV + uvStep * LAST_EDGE_STEP_GUESS; + } + + vec2 uvN = edgeUV - uvStep; + float maskDeltaN = getLineMaskValueOffset(uvN) - edgeMask; + bool atEndN = abs(maskDeltaN) >= gradientThreshold; + + for (i = 0; i < 10 && !atEndN; i++) { + uvN -= uvStep * EDGE_STEP_SIZES_ARRAY[i]; + maskDeltaN = getLineMaskValueOffset(uvN) - edgeMask; + atEndN = abs(maskDeltaN) >= gradientThreshold; + } + if (!atEndN) { + uvN = edgeUV - uvStep * LAST_EDGE_STEP_GUESS; + } + + float distanceToEndP, distanceToEndN; + if (edge.isHorizontal) { + distanceToEndP = uvP.x - uv.x; + distanceToEndN = uv.x - uvN.x; + } else { + distanceToEndP = uvP.y - uv.y; + distanceToEndN = uv.y - uvN.y; + } + + float distanceToNearestEnd; + bool deltaSign; + if (distanceToEndP <= distanceToEndN) { + distanceToNearestEnd = distanceToEndP; + deltaSign = maskDeltaP >= 0; + } else { + distanceToNearestEnd = distanceToEndN; + deltaSign = maskDeltaN >= 0; + } + + if (deltaSign == (neighbor.center - edgeMask >= 0)) { + return 0.0; + } else { + return 0.5 - distanceToNearestEnd / (distanceToEndP + distanceToEndN); + } +} + +float getFillMask(ivec2 iuv) { + MaskNeighbor neighbor = getFillMaskNeighbor(iuv); + if (canSkipFxaa(neighbor)) { + return neighbor.center; + } + + vec2 uv = vec2(iuv) / vec2(pushConstants.width, pushConstants.height); + + FxaaEdge edge = getFxaaEdge(neighbor); + float blendFactor = max(getSubpixelBlendFactor(neighbor), getFillEdgeBlendFactor(neighbor, edge, uv)); + if (edge.isHorizontal) { + vec2 offset = vec2(0.0, blendFactor) / vec2(pushConstants.width, pushConstants.height); + return getFillMaskValueOffset(uv + offset); + } else { + vec2 offset = vec2(blendFactor, 0.0) / vec2(pushConstants.width, pushConstants.height); + return getFillMaskValueOffset(uv + offset); + } +} + +float getLineMask(ivec2 iuv) { + MaskNeighbor neighbor = getLineMaskNeighbor(iuv); + if (canSkipFxaa(neighbor)) { + return neighbor.center; + } + + vec2 uv = vec2(iuv) / vec2(pushConstants.width, pushConstants.height); + + FxaaEdge edge = getFxaaEdge(neighbor); + float blendFactor = max(getSubpixelBlendFactor(neighbor), getLineEdgeBlendFactor(neighbor, edge, uv)); + if (edge.isHorizontal) { + vec2 offset = vec2(0.0, blendFactor) / vec2(pushConstants.width, pushConstants.height); + return getLineMaskValueOffset(uv + offset); + } else { + vec2 offset = vec2(blendFactor, 0.0) / vec2(pushConstants.width, pushConstants.height); + return getLineMaskValueOffset(uv + offset); + } +} + +vec4 getFillColor() { + vec4 color; + if (pushConstants.fillMaterialType == 0) { + color.r = float(pushConstants.fillR8) / 255.0; + color.g = float(pushConstants.fillG8) / 255.0; + color.b = float(pushConstants.fillB8) / 255.0; + color.a = float(pushConstants.fillA8) / 255.0; + } else if (pushConstants.fillMaterialType == 1) { + vec2 v = pushConstants.fillPos1 - pushConstants.fillPos0; + vec2 uv = vec2(gl_GlobalInvocationID.xy) / 2.0; + float t = dot(uv - pushConstants.fillPos0, v) / dot(v, v); + color = texture(fillGradientTexture, vec2(t, 0.5)); + } else if (pushConstants.fillMaterialType == 2) { + vec2 a = pushConstants.fillPos1 - pushConstants.fillPos0; + vec2 b = pushConstants.fillPos2 - pushConstants.fillPos0; + float aLength = length(a); + float bLength = length(b); + vec2 aNorm = a / aLength; + vec2 bNorm = b / bLength; + if (aLength < 0.0001 || bLength < 0.0001) { + color = texture(fillGradientTexture, vec2(1.0, 0.5)); + } else { + vec2 uv = vec2(gl_GlobalInvocationID.xy) / 2.0; + vec2 p = uv - pushConstants.fillPos0; + vec2 pp = dot(p, aNorm) * aNorm + dot(p, bNorm) * bNorm / bLength * aLength; + float t = length(pp) / aLength; + color = texture(fillGradientTexture, vec2(t, 0.5)); + } + } else if (pushConstants.fillMaterialType == 3) { + vec2 checker_count = vec2(pushConstants.width, pushConstants.height) / 96.0; + vec2 uv = vec2(gl_GlobalInvocationID.xy) / vec2(pushConstants.width, pushConstants.height); + float checker = mod(dot(vec2(1.0), step(vec2(0.5), fract(uv * checker_count))), 2.0); + color.rgb = mix(vec3(1.0, 0.0, 1.0), vec3(0.0, 1.0, 1.0), checker); + color.a = 1.0; + } + return color; +} + +vec4 getLineColor() { + vec4 color; + if (pushConstants.lineMaterialType == 0) { + color.r = float(pushConstants.lineR8) / 255.0; + color.g = float(pushConstants.lineG8) / 255.0; + color.b = float(pushConstants.lineB8) / 255.0; + color.a = float(pushConstants.lineA8) / 255.0; + } else if (pushConstants.lineMaterialType == 1) { + vec2 v = pushConstants.linePos1 - pushConstants.linePos0; + vec2 uv = vec2(gl_GlobalInvocationID.xy) / 2.0; + float t = dot(uv - pushConstants.linePos0, v) / dot(v, v); + color = texture(lineGradientTexture, vec2(t, 0.5)); + } else if (pushConstants.lineMaterialType == 2) { + vec2 a = pushConstants.linePos1 - pushConstants.linePos0; + vec2 b = pushConstants.linePos2 - pushConstants.linePos0; + float aLength = length(a); + float bLength = length(b); + vec2 aNorm = a / aLength; + vec2 bNorm = b / bLength; + if (aLength < 0.0001 || bLength < 0.0001) { + color = texture(lineGradientTexture, vec2(1.0, 0.5)); + } else { + vec2 uv = vec2(gl_GlobalInvocationID.xy) / 2.0; + vec2 p = uv - pushConstants.linePos0; + vec2 pp = dot(p, aNorm) * aNorm + dot(p, bNorm) * bNorm / bLength * aLength; + float t = length(pp) / aLength; + color = texture(lineGradientTexture, vec2(t, 0.5)); + } + } else if (pushConstants.lineMaterialType == 3) { + vec2 checker_count = vec2(pushConstants.width, pushConstants.height) / 96.0; + vec2 uv = vec2(gl_GlobalInvocationID.xy) / vec2(pushConstants.width, pushConstants.height); + float checker = mod(dot(vec2(1.0), step(vec2(0.5), fract(uv * checker_count))), 2.0); + color.rgb = mix(vec3(1.0, 0.0, 1.0), vec3(0.0, 1.0, 1.0), checker); + color.a = 1.0; + } + return color; +} + +void main() { + ivec2 size = ivec2(pushConstants.width, pushConstants.height); + ivec2 uv = ivec2(gl_GlobalInvocationID.xy); + + if (uv.x >= size.x || uv.y >= size.y) { + return; + } + + vec4 fillColor = getFillColor(); + vec4 lineColor = getLineColor(); + fillColor.a *= getFillMask(uv); + lineColor.a *= getLineMask(uv); + + vec4 color; + color.a = fillColor.a * (1.0 - lineColor.a) + lineColor.a; + if (color.a != 0) { + color.rgb = (fillColor.rgb * (1 - lineColor.a) * fillColor.a + lineColor.rgb * lineColor.a) / color.a; + } else { + color.rgb = vec3(0); + } + + imageStore(store_image, uv, color); +} diff --git a/godot/shaders/composite/path_composite_jfa_line.glsl.import b/godot/shaders/composite/path_composite_jfa_line.glsl.import new file mode 100644 index 0000000..188f9df --- /dev/null +++ b/godot/shaders/composite/path_composite_jfa_line.glsl.import @@ -0,0 +1,14 @@ +[remap] + +importer="glsl" +type="RDShaderFile" +uid="uid://ci8r0pixjqqwc" +path="res://.godot/imported/path_composite_jfa_line.glsl-40357f493df49d726c2dde11e935d04e.res" + +[deps] + +source_file="res://shaders/composite/path_composite_jfa_line.glsl" +dest_files=["res://.godot/imported/path_composite_jfa_line.glsl-40357f493df49d726c2dde11e935d04e.res"] + +[params] + diff --git a/godot/shaders/composite/rasterize.glsl b/godot/shaders/composite/rasterize.glsl new file mode 100644 index 0000000..b1f6e60 --- /dev/null +++ b/godot/shaders/composite/rasterize.glsl @@ -0,0 +1,55 @@ +#[vertex] +#version 460 + +layout(location = 0) in vec2 position; + +layout(push_constant, std430) uniform PushConstants { + int width; + int height; + int booleanMode; + int dummy; +} pushConstants; + +void main() +{ + vec2 pos = position / vec2(pushConstants.width, pushConstants.height); + pos = pos * 2.0 - 1.0; + gl_Position = vec4(pos, 0.0, 1.0); +} + + +#[fragment] +#version 460 + +layout (location = 0) out vec4 out_color; + +layout(set = 0, binding = 0) uniform sampler2D backBufferImage; + +layout(push_constant, std430) uniform PushConstants { + int width; + int height; + int booleanMode; + int dummy; +} pushConstants; + +void main() +{ + vec2 rg = texture(backBufferImage, gl_FragCoord.xy / vec2(pushConstants.width, pushConstants.height)).rg; + + if (pushConstants.booleanMode == 0) { + // Add + out_color = vec4(1, 0, 0, 0); + } else if (pushConstants.booleanMode == 1) { + // Diff + out_color = vec4(0, 0, 0, 0); + } else if (pushConstants.booleanMode == 2) { + // Intersect first pass + out_color = vec4(rg.r, 1, 0, 0); + } else if (pushConstants.booleanMode == 3) { + // Xor + out_color = vec4(1 - rg.r, 0, 0, 0); + } else { + // Intersect second pass + out_color = vec4(rg.r* rg.g, 0, 0, 0); + } +} diff --git a/godot/shaders/composite/rasterize.glsl.import b/godot/shaders/composite/rasterize.glsl.import new file mode 100644 index 0000000..21c2467 --- /dev/null +++ b/godot/shaders/composite/rasterize.glsl.import @@ -0,0 +1,14 @@ +[remap] + +importer="glsl" +type="RDShaderFile" +uid="uid://doe67sucfd7sb" +path="res://.godot/imported/rasterize.glsl-a916df1092d3c270aa536fde6c4ecaa3.res" + +[deps] + +source_file="res://shaders/composite/rasterize.glsl" +dest_files=["res://.godot/imported/rasterize.glsl-a916df1092d3c270aa536fde6c4ecaa3.res"] + +[params] + diff --git a/godot/shaders/fill.gdshader b/godot/shaders/fill.gdshader new file mode 100644 index 0000000..e4d3cbb --- /dev/null +++ b/godot/shaders/fill.gdshader @@ -0,0 +1,9 @@ +shader_type canvas_item; +render_mode unshaded; + +uniform vec4 fill_color: source_color = vec4(1.0, 1.0, 1.0, 1.0); + +void fragment() { + COLOR.rgb = fill_color.rgb; + COLOR.a *= fill_color.a; +} diff --git a/godot/shaders/fill_checker_alpha.gdshader b/godot/shaders/fill_checker_alpha.gdshader new file mode 100644 index 0000000..0d7377f --- /dev/null +++ b/godot/shaders/fill_checker_alpha.gdshader @@ -0,0 +1,15 @@ +shader_type canvas_item; +render_mode unshaded; + +uniform vec4 fill_color: source_color = vec4(1.0, 1.0, 1.0, 1.0); +uniform vec2 document_size = vec2(480, 720); +uniform float document_scale = 1.0; + +void fragment() { + vec2 checker_count = document_size * document_scale / 20.0; + float checker = mod(dot(vec2(1.0), step(vec2(0.5), fract(UV * checker_count))), 2.0); + vec3 checker_color = mix(vec3(1.0, 1.0, 1.0), vec3(0.8, 0.8, 0.8), checker); + + COLOR.rgb = fill_color.rgb * fill_color.a + checker_color * (1.0 - fill_color.a); + COLOR.a = 1.0; +} diff --git a/godot/shaders/linear_gradient_checker_alpha.gdshader b/godot/shaders/linear_gradient_checker_alpha.gdshader new file mode 100644 index 0000000..617172c --- /dev/null +++ b/godot/shaders/linear_gradient_checker_alpha.gdshader @@ -0,0 +1,17 @@ +shader_type canvas_item; +render_mode unshaded; + +uniform sampler2D gradient_texture; +uniform vec2 document_size = vec2(480, 720); +uniform float document_scale = 1.0; + +void fragment() { + vec4 gradient_color = texture(gradient_texture, vec2(UV.x, 0.5)); + + vec2 checker_count = document_size * document_scale / 20.0; + float checker = mod(dot(vec2(1.0), step(vec2(0.5), fract(UV * checker_count))), 2.0); + vec3 checker_color = mix(vec3(1.0, 1.0, 1.0), vec3(0.8, 0.8, 0.8), checker); + + COLOR.rgb = gradient_color.rgb * gradient_color.a + checker_color * (1.0 - gradient_color.a); + COLOR.a = 1.0; +} diff --git a/godot/shaders/linear_gradient_path.gdshader b/godot/shaders/linear_gradient_path.gdshader new file mode 100644 index 0000000..82c7867 --- /dev/null +++ b/godot/shaders/linear_gradient_path.gdshader @@ -0,0 +1,12 @@ +shader_type canvas_item; +render_mode unshaded; + +uniform sampler2D gradient_texture; +uniform vec2 start_point = vec2(0, 0); +uniform vec2 end_point = vec2(1, 0); + +void fragment() { + vec2 v = end_point - start_point; + float t = dot(SCREEN_UV - start_point, v) / dot(v, v); + COLOR = texture(gradient_texture, vec2(t, 0.5)); +} diff --git a/godot/shaders/missing_material.gdshader b/godot/shaders/missing_material.gdshader new file mode 100644 index 0000000..5b35429 --- /dev/null +++ b/godot/shaders/missing_material.gdshader @@ -0,0 +1,11 @@ +shader_type canvas_item; +render_mode unshaded; + +void fragment() { + vec2 checker_count = vec2(1.0) / SCREEN_PIXEL_SIZE / 24.0; + float checker = mod(dot(vec2(1.0), step(vec2(0.5), fract(SCREEN_UV * checker_count))), 2.0); + vec3 checker_color = mix(vec3(1.0, 0.0, 1.0), vec3(0.0, 1.0, 1.0), checker); + + COLOR.rgb = checker_color; + COLOR.a = 1.0; +} diff --git a/godot/shaders/radial_gradient_checker_alpha.gdshader b/godot/shaders/radial_gradient_checker_alpha.gdshader new file mode 100644 index 0000000..6df2762 --- /dev/null +++ b/godot/shaders/radial_gradient_checker_alpha.gdshader @@ -0,0 +1,18 @@ +shader_type canvas_item; +render_mode unshaded; + +uniform sampler2D gradient_texture; +uniform vec2 document_size = vec2(480, 720); +uniform float document_scale = 1.0; + +void fragment() { + vec2 uv = vec2(length(UV - vec2(0.5)) * 2.0, 0.5); + vec4 gradient_color = texture(gradient_texture, uv); + + vec2 checker_count = document_size * document_scale / 20.0; + float checker = mod(dot(vec2(1.0), step(vec2(0.5), fract(UV * checker_count))), 2.0); + vec3 checker_color = mix(vec3(1.0, 1.0, 1.0), vec3(0.8, 0.8, 0.8), checker); + + COLOR.rgb = gradient_color.rgb * gradient_color.a + checker_color * (1.0 - gradient_color.a); + COLOR.a = 1.0; +} diff --git a/godot/shaders/radial_gradient_path.gdshader b/godot/shaders/radial_gradient_path.gdshader new file mode 100644 index 0000000..cc63704 --- /dev/null +++ b/godot/shaders/radial_gradient_path.gdshader @@ -0,0 +1,25 @@ +shader_type canvas_item; +render_mode unshaded; + +uniform sampler2D gradient_texture; +uniform vec2 center_point = vec2(0, 0); +uniform vec2 handle_1_point = vec2(1, 0); +uniform vec2 handle_2_point = vec2(1, 0); + +void fragment() { + vec2 a = handle_1_point - center_point; + vec2 b = handle_2_point - center_point; + float aLength = length(a); + float bLength = length(b); + vec2 aNorm = a / aLength; + vec2 bNorm = b / bLength; + if (aLength < 0.0001 || bLength < 0.0001) { + COLOR = texture(gradient_texture, vec2(1.0, 0.5)); + } else { + vec2 uv = SCREEN_UV; + vec2 p = uv - center_point; + vec2 pp = dot(p, aNorm) * aNorm + dot(p, bNorm) * bNorm / bLength * aLength; + float t = length(pp) / aLength; + COLOR = texture(gradient_texture, vec2(t, 0.5)); + } +} diff --git a/godot/shaders/texture_checker_alpha.gdshader b/godot/shaders/texture_checker_alpha.gdshader new file mode 100644 index 0000000..1167563 --- /dev/null +++ b/godot/shaders/texture_checker_alpha.gdshader @@ -0,0 +1,19 @@ +shader_type canvas_item; +render_mode unshaded; + +uniform sampler2D main_texture: hint_default_transparent, filter_linear_mipmap, source_color; +uniform vec2 document_size = vec2(480, 720); +uniform float document_scale = 1.0; + +void fragment() { + vec4 main_color = texture(main_texture, UV); + + vec2 checker_count = vec2(1.0) / SCREEN_PIXEL_SIZE / 24.0; + float checker = mod(dot(vec2(1.0), step(vec2(0.5), fract(SCREEN_UV * checker_count))), 2.0); + vec3 checker_color = mix(vec3(1.0, 1.0, 1.0), vec3(0.8, 0.8, 0.8), checker); + + // Godot標準のpre-multiply alphaテクスチャではなく、 + // 自前のcompositeしたテクスチャを使うので通常のalpha計算を行っている + COLOR.rgb = main_color.rgb * main_color.a + checker_color * (1.0 - main_color.a); + COLOR.a = 1.0; +} diff --git a/godot/shape.gdextension b/godot/shape.gdextension new file mode 100644 index 0000000..cd0af34 --- /dev/null +++ b/godot/shape.gdextension @@ -0,0 +1,14 @@ +[configuration] +entry_symbol = "gdext_rust_init" +compatibility_minimum = 4.3 +reloadable = true + +[libraries] +linux.debug.x86_64 = "res://../rust/target/debug/libshape.so" +linux.release.x86_64 = "res://../rust/target/release/libshape.so" +windows.debug.x86_64 = "res://../rust/target/debug/shape.dll" +windows.release.x86_64 = "res://../rust/target/release/shape.dll" +macos.debug = "res://../rust/target/debug/libshape.dylib" +macos.release = "res://../rust/target/release/libshape.dylib" +macos.debug.arm64 = "res://../rust/target/debug/libshape.dylib" +macos.release.arm64 = "res://../rust/target/release/libshape.dylib" diff --git a/godot/splash-logo.png b/godot/splash-logo.png new file mode 100644 index 0000000..9332352 Binary files /dev/null and b/godot/splash-logo.png differ diff --git a/godot/splash-logo.png.import b/godot/splash-logo.png.import new file mode 100644 index 0000000..c99f6b8 --- /dev/null +++ b/godot/splash-logo.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ckx8xvgp5dj2f" +path="res://.godot/imported/splash-logo.png-67cae680c6a5e4292ece44bb6fc55b78.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://splash-logo.png" +dest_files=["res://.godot/imported/splash-logo.png-67cae680c6a5e4292ece44bb6fc55b78.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/godot/src/app.gd b/godot/src/app.gd new file mode 100644 index 0000000..fb065b6 --- /dev/null +++ b/godot/src/app.gd @@ -0,0 +1,228 @@ +class_name App +extends PanelContainer + + +@onready var _file_menu_button: MenuButton = %FileMenuButton +@onready var _edit_menu_button: MenuButton = %EditMenuButton +@onready var _tool_menu_button: MenuButton = %ToolMenuButton +@onready var _document_open_window: DocumentOpenWindow = %DocumentOpenWindow +@onready var _document_export_window: DocumentExportWindow = %DocumentExportWindow +@onready var _document_size_change_window: DocumentSizeChangeWindow = %DocumentSizeChangeWindow +@onready var _configwindow: ConfigWindow = %ConfigWindow +@onready var _licenses_window: LicensesWindow = %LicensesWindow +@onready var _file_dialog: FileDialog = %FileDialog +@onready var _close_confirm_dialog: ConfirmationDialog = %CloseConfirmationDialog +@onready var _canvas_tab: CanvasTab = %CanvasTab +@onready var _layer_list_tab: LayerListTab = %Layer +@onready var _layer_setting_tab: LayerSettingTab = %LayerSetting +@onready var _material_list_tab: MaterialListTab = %MaterialList + +## 破棄するかのダイアログの結果のシグナル。 +signal on_confirm_close(discard: bool) + + +func _ready() -> void: + DisplayServer.window_set_title("AquamarinePainter") + Main.on_document_dirty_changed.connect(_document_dirty_changed) + + _file_menu_button.get_popup().id_pressed.connect(_file_menu) + _edit_menu_button.get_popup().id_pressed.connect(_edit_menu) + _tool_menu_button.get_popup().id_pressed.connect(_tool_menu) + + _close_confirm_dialog.confirmed.connect(func() -> void: on_confirm_close.emit(true)) + _close_confirm_dialog.canceled.connect(func() -> void: on_confirm_close.emit(false)) + + # 閉じるボタンをしても閉じないようにする + get_tree().set_auto_accept_quit(false) + + +func _input(event: InputEvent) -> void: + if event.is_action_pressed("save"): + _document_save() + elif event.is_action_pressed("save_as"): + _document_save_as() + elif event.is_action_pressed("redo"): + Main.redo_history() + elif event.is_action_pressed("undo"): + Main.undo_hisotry() + elif event.is_action_pressed("ui_copy"): + Main.copy_manager.copy() + elif event.is_action_pressed("ui_paste"): + Main.materials.paste_copied_material() + Main.layers.paste_copied_layers() + + +func _notification(what: int) -> void: + # ウィンドウの閉じるボタンが押されたときの確認ダイアログ + if what == NOTIFICATION_WM_CLOSE_REQUEST: + if not Main.document_opened or not Main.document_dirty: + get_tree().quit() + else: + _close_confirm_dialog.show() + if await on_confirm_close: + get_tree().quit() + + +## 操作中かどうかを計算する。 +func get_is_manipulating() -> bool: + var is_manipulating := false + is_manipulating = is_manipulating or _canvas_tab.get_manipulating() + is_manipulating = is_manipulating or _material_list_tab.is_manipulate_color() + is_manipulating = is_manipulating or _layer_list_tab.is_manipulating_alpha() + is_manipulating = is_manipulating or _layer_setting_tab.is_manipulating_line_width() + return is_manipulating + + +## ドキュメントのdirtyが切り替わったときに呼び出すコールバック。 +func _document_dirty_changed() -> void: + if Main.document_file_path.is_empty(): + DisplayServer.window_set_title("AquamarinePainter") + else: + var filename := Main.document_file_path.split("/")[-1] + if Main.document_dirty: + filename = "* " + filename + DisplayServer.window_set_title(filename + " - AquamarinePainter") + + +## ファイルメニューのコールバック。 +func _file_menu(id: int) -> void: + match id: + 0: + # 新規ドキュメント + if not Main.document_opened or not Main.document_dirty: + _document_open_window.set_document_size(Vector2(800, 600)) + _document_open_window.show() + else: + _close_confirm_dialog.show() + if await on_confirm_close: + _document_open_window.set_document_size(Vector2(800, 600)) + _document_open_window.show() + 1: + # ドキュメントを開く + if not Main.document_opened or not Main.document_dirty: + _document_open() + else: + _close_confirm_dialog.show() + if await on_confirm_close: + _document_open() + 2: + # 上書き保存 + _document_save() + 3: + # 別名で保存 + _document_save_as() + 4: + # ドキュメントを閉じる + if not Main.document_dirty: + Main.close_document() + else: + _close_confirm_dialog.show() + if await on_confirm_close: + Main.close_document() + 6: + # エクスポート + if Main.document_opened: + _document_export_window.set_document_size(Main.document_size) + _document_export_window.show() + + +## 編集メニューのコールバック。 +func _edit_menu(id: int) -> void: + match id: + 0: + # 元に戻す + Main.undo_hisotry() + 1: + # やり直し + Main.redo_history() + 3: + # ドキュメントサイズの変更 + if Main.document_opened: + _document_size_change_window.set_document_size_and_anchor( + Main.document_size, PaintLayer.ScaleAnchor.Center) + _document_size_change_window.show() + + +## ツールメニューのコールバック。 +func _tool_menu(id: int) -> void: + match id: + 0: + # 環境設定 + _configwindow.show_config() + 1: + # ライセンス + _licenses_window.show() + + +## ドキュメントを読み込む。 +func _document_open() -> void: + _file_dialog.clear_filters() + _file_dialog.add_filter("*.ampi", "AquamarinePainter Document") + _file_dialog.file_mode = FileDialog.FILE_MODE_OPEN_FILE + _file_dialog.current_file = "" + _file_dialog.show() + if not _file_dialog.current_file.is_empty(): + Main.load_document(_file_dialog.current_file) + + +## ドキュメントを上書き保存する。 +func _document_save() -> void: + Main.save_document() + + +## ドキュメントを別名で保存する。 +func _document_save_as() -> void: + if not Main.document_opened: + return + _file_dialog.clear_filters() + _file_dialog.add_filter("*.ampi", "AquamarinePainter Document") + _file_dialog.file_mode = FileDialog.FILE_MODE_SAVE_FILE + _file_dialog.current_file = "" + _file_dialog.show() + if not _file_dialog.current_file.is_empty(): + Main.save_as_document(_file_dialog.current_file) + + +## ドキュメントを新しく作成するコールバック。 +func _on_document_open_window_on_create_document(document_size: Vector2) -> void: + _file_dialog.clear_filters() + _file_dialog.add_filter("*.ampi", "AquamarinePainter Document") + _file_dialog.file_mode = FileDialog.FILE_MODE_SAVE_FILE + _file_dialog.current_file = "" + _file_dialog.show() + if not _file_dialog.current_file.is_empty(): + Main.create_document(document_size, _file_dialog.current_file) + + +## ドキュメントをpngでエクスポートする。 +func _on_document_export_window_on_export_document(document_size: Vector2) -> void: + _file_dialog.clear_filters() + _file_dialog.add_filter("*.png", "Portable Network Graphics") + _file_dialog.file_mode = FileDialog.FILE_MODE_SAVE_FILE + _file_dialog.current_file = "" + _file_dialog.show() + if not _file_dialog.current_file.is_empty(): + Main.export_document(_file_dialog.current_file, document_size) + + +## ドキュメントサイズ変更ウィンドウのコールバック。 +func _on_document_size_change_window_on_change_document_size( + document_size: Vector2, anchor: PaintLayer.ScaleAnchor) -> void: + Main.copy_manager.change_document_size(document_size, anchor) + Main.materials.change_document_size(document_size, anchor) + Main.layers.change_document_size(document_size, anchor) + Main.compositor.resize(document_size) + Main.document_size = document_size + + # レイヤーリストのサムネのテクスチャがinvalidになるので、一旦更新する + Main.emit_update_layer_list_tab() + + # compositが成功するまでフレームを進める + while not Main.compositor.composite(Main.layers.root, true): + await (Engine.get_main_loop() as SceneTree).process_frame + Main.compositor.step_create_texture() + + # 改めてレイヤーリストのサムネの更新を行う + Main.emit_update_layer_list_tab() + + Main.commit_history() diff --git a/godot/src/compositor/fill_paint_compositor.gd b/godot/src/compositor/fill_paint_compositor.gd new file mode 100644 index 0000000..23c356f --- /dev/null +++ b/godot/src/compositor/fill_paint_compositor.gd @@ -0,0 +1,160 @@ +## 塗りつぶしレイヤーを描画成するcompositor。 +class_name FillPaintCompositor +extends RefCounted + + +var texture_size: Vector2i + +## コンピュートシェーダー。 +var _shader: RID +## コンピュートパイプライン。 +var _pipeline: RID + +## サンプラー。 +var _sampler: RID + + +func _init() -> void: + RenderingServer.call_on_render_thread(_initialize_compute_shader) + + +func _notification(what: int) -> void: + if what == NOTIFICATION_PREDELETE: + var free_compute_resource := func(shader: RID, sampler: RID) -> void: + var rd := RenderingServer.get_rendering_device() + if shader: + rd.free_rid(shader) + if sampler: + rd.free_rid(sampler) + RenderingServer.call_on_render_thread(free_compute_resource.bind(_shader, _sampler)) + + +func _initialize_compute_shader() -> void: + var rd := RenderingServer.get_rendering_device() + + var shader_file: RDShaderFile = load("res://shaders/composite/fill_composite.glsl") + var shader_spirv := shader_file.get_spirv() + _shader = rd.shader_create_from_spirv(shader_spirv) + _pipeline = rd.compute_pipeline_create(_shader) + + var sampler_state := RDSamplerState.new() + sampler_state.min_filter = RenderingDevice.SAMPLER_FILTER_LINEAR + sampler_state.mag_filter = RenderingDevice.SAMPLER_FILTER_LINEAR + sampler_state.mip_filter = RenderingDevice.SAMPLER_FILTER_LINEAR + _sampler = rd.sampler_create(sampler_state) + + +func resize(size: Vector2i) -> void: + if texture_size != size: + texture_size = size + + +func _render_process(output_texture: PaintCompositor.TextureHandle, fill_material: PaintMaterial) -> void: + var rd := RenderingServer.get_rendering_device() + + if not rd.texture_is_valid(output_texture.texture_rid): + return + + var x_group := (texture_size.x * 1 - 1) / 8 + 1 + var y_group := (texture_size.y * 1 - 1) / 8 + 1 + + var texture_uniform := RDUniform.new() + texture_uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_IMAGE + texture_uniform.binding = 0 + texture_uniform.add_id(output_texture.texture_rid) + var texture_set := rd.uniform_set_create([texture_uniform], _shader, 0) + rd.texture_clear(output_texture.texture_rid, Color(0, 0, 0, 0), 0, 1, 0, 1) + + var gradient_texture_uniform := RDUniform.new() + gradient_texture_uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_SAMPLER_WITH_TEXTURE + gradient_texture_uniform.binding = 0 + gradient_texture_uniform.add_id(_sampler) + if fill_material is ColorPaintMaterial: + gradient_texture_uniform.add_id(RenderingServer.texture_get_rd_texture(RenderingServer.get_white_texture())) + elif fill_material is LinearGradientPaintMaterial: + var linear_gradient_material := fill_material as LinearGradientPaintMaterial + var gradient_rid := RenderingServer.texture_get_rd_texture(linear_gradient_material.gradient_texture) + gradient_texture_uniform.add_id(gradient_rid) + elif fill_material is RadialGradientPaintMaterial: + var radial_gradient_material := fill_material as RadialGradientPaintMaterial + var gradient_rid := RenderingServer.texture_get_rd_texture(radial_gradient_material.gradient_texture) + gradient_texture_uniform.add_id(gradient_rid) + elif fill_material == null: + gradient_texture_uniform.add_id(RenderingServer.texture_get_rd_texture(RenderingServer.get_white_texture())) + var gradient_texture_set := rd.uniform_set_create([gradient_texture_uniform], _shader, 1) + + # push_counstantsを詰める + var push_constant := PackedInt32Array() + push_constant.push_back(texture_size.x * 1) + push_constant.push_back(texture_size.y * 1) + + if fill_material is ColorPaintMaterial: + var color_material := fill_material as ColorPaintMaterial + push_constant.push_back(0) # material type + push_constant.push_back(color_material.color.r8) + push_constant.push_back(color_material.color.g8) + push_constant.push_back(color_material.color.b8) + push_constant.push_back(color_material.color.a8) + push_constant.push_back(0) # dummmy + var pos_bytes := PackedVector2Array([ + Vector2(0, 0), + Vector2(0, 0), + Vector2(0, 0), + ]).to_byte_array().to_int32_array() + push_constant.append_array(pos_bytes) + elif fill_material is LinearGradientPaintMaterial: + var linear_gradient_material := fill_material as LinearGradientPaintMaterial + push_constant.push_back(1) # material type + push_constant.push_back(0) + push_constant.push_back(0) + push_constant.push_back(0) + push_constant.push_back(0) + push_constant.push_back(0) # dummmy + var pos_bytes := PackedVector2Array([ + linear_gradient_material.start_point, + linear_gradient_material.end_point, + Vector2(0, 0), + ]).to_byte_array().to_int32_array() + push_constant.append_array(pos_bytes) + elif fill_material is RadialGradientPaintMaterial: + var radial_gradient_material := fill_material as RadialGradientPaintMaterial + push_constant.push_back(2) # material type + push_constant.push_back(0) + push_constant.push_back(0) + push_constant.push_back(0) + push_constant.push_back(0) + push_constant.push_back(0) # dummmy + var pos_bytes := PackedVector2Array([ + radial_gradient_material.center_point, + radial_gradient_material.handle_1_point, + radial_gradient_material.handle_2_point, + ]).to_byte_array().to_int32_array() + push_constant.append_array(pos_bytes) + elif fill_material == null: + push_constant.push_back(3) # material type + push_constant.push_back(0) + push_constant.push_back(0) + push_constant.push_back(0) + push_constant.push_back(0) + push_constant.push_back(0) # dummmy + var pos_bytes := PackedVector2Array([ + Vector2(0, 0), + Vector2(0, 0), + Vector2(0, 0), + ]).to_byte_array().to_int32_array() + push_constant.append_array(pos_bytes) + push_constant.push_back(0) # dummmy + push_constant.push_back(0) # dummmy + + # コマンドを発行 + var compute_list := rd.compute_list_begin() + rd.compute_list_bind_compute_pipeline(compute_list, _pipeline) + rd.compute_list_bind_uniform_set(compute_list, texture_set, 0) + rd.compute_list_bind_uniform_set(compute_list, gradient_texture_set, 1) + rd.compute_list_set_push_constant(compute_list, push_constant.to_byte_array(), push_constant.size() * 4) + rd.compute_list_dispatch(compute_list, x_group, y_group, 1) + rd.compute_list_end() + + +func composite(output_texture: PaintCompositor.TextureHandle, fill_material: PaintMaterial) -> void: + RenderingServer.call_on_render_thread(_render_process.bind(output_texture, fill_material)) diff --git a/godot/src/compositor/group_paint_compositor.gd b/godot/src/compositor/group_paint_compositor.gd new file mode 100644 index 0000000..f979ea2 --- /dev/null +++ b/godot/src/compositor/group_paint_compositor.gd @@ -0,0 +1,112 @@ +## 複数のレイヤーをアルファブレンドしながら合成するcompositor。 +class_name GroupPaintCompositor +extends RefCounted + + +var texture_size: Vector2i + +## コンピュートシェーダー。 +var _shader: RID +## コンピュートパイプライン。 +var _pipeline: RID + + +func _init() -> void: + RenderingServer.call_on_render_thread(_initialize_compute_shader) + + +func _notification(what: int) -> void: + if what == NOTIFICATION_PREDELETE: + var free_compute_resource := func(shader: RID) -> void: + var rd := RenderingServer.get_rendering_device() + if shader: + rd.free_rid(shader) + RenderingServer.call_on_render_thread(free_compute_resource.bind(_shader)) + + +func _initialize_compute_shader() -> void: + var rd := RenderingServer.get_rendering_device() + + var shader_file: RDShaderFile = load("res://shaders/composite/group_composite.glsl") + var shader_spirv := shader_file.get_spirv() + _shader = rd.shader_create_from_spirv(shader_spirv) + _pipeline = rd.compute_pipeline_create(_shader) + + +func resize(size: Vector2i) -> void: + if texture_size != size: + texture_size = size + + +func _render_process(output_texture: PaintCompositor.TextureHandle, texture_rids: Array[RID], blend_modes: Array[PaintLayer.BlendMode], clippings: Array[bool], alphas: Array[int]) -> void: + var rd := RenderingServer.get_rendering_device() + + if not rd.texture_is_valid(output_texture.texture_rid): + return + for rid in texture_rids: + if not rd.texture_is_valid(rid): + return + + var x_group := (texture_size.x * 1 - 1) / 8 + 1 + var y_group := (texture_size.y * 1 - 1) / 8 + 1 + + var texture_uniform := RDUniform.new() + texture_uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_IMAGE + texture_uniform.binding = 0 + texture_uniform.add_id(output_texture.texture_rid) + var texture_set := rd.uniform_set_create([texture_uniform], _shader, 0) + rd.texture_clear(output_texture.texture_rid, Color(0, 0, 0, 0), 0, 1, 0, 1) + + for index in texture_rids.size(): + # 合成のforeground画像をbindする + var foreground := RDUniform.new() + foreground.uniform_type = RenderingDevice.UNIFORM_TYPE_IMAGE + foreground.binding = 0 + foreground.add_id(texture_rids[index]) + var foreground_set := rd.uniform_set_create([foreground], _shader, 1) + + # 合成のclippingの親に当たる画像を探してbindする + var clipping_index := -1 + if clippings[index]: + for i in index: + if not clippings[index - i - 1]: + clipping_index = index - i - 1 + break + var clipping := RDUniform.new() + clipping.uniform_type = RenderingDevice.UNIFORM_TYPE_IMAGE + clipping.binding = 0 + if clipping_index == -1: + clipping.add_id(output_texture.texture_rid) + else: + clipping.add_id(texture_rids[clipping_index]) + var clipping_set := rd.uniform_set_create([clipping], _shader, 2) + + # クリッピングのアルファ値も計算する + var clipping_alpha := 100 + if clipping_index != -1: + clipping_alpha = alphas[clipping_index] + + # push_counstantsを詰める + var push_constant := PackedInt32Array() + push_constant.push_back(blend_modes[index]) + push_constant.push_back(clippings[index]) + push_constant.push_back(texture_size.x * 1) + push_constant.push_back(texture_size.y * 1) + push_constant.push_back(alphas[index]) + push_constant.push_back(clipping_alpha) + push_constant.push_back(0) + push_constant.push_back(0) # dummy + + # コマンドを発行 + var compute_list := rd.compute_list_begin() + rd.compute_list_bind_compute_pipeline(compute_list, _pipeline) + rd.compute_list_bind_uniform_set(compute_list, texture_set, 0) + rd.compute_list_bind_uniform_set(compute_list, foreground_set, 1) + rd.compute_list_bind_uniform_set(compute_list, clipping_set, 2) + rd.compute_list_set_push_constant(compute_list, push_constant.to_byte_array(), push_constant.size() * 4) + rd.compute_list_dispatch(compute_list, x_group, y_group, 1) + rd.compute_list_end() + + +func composite(output_texture: PaintCompositor.TextureHandle, texture_rids: Array[RID], blend_modes: Array[PaintLayer.BlendMode], clippings: Array[bool], alphas: Array[int]) -> void: + RenderingServer.call_on_render_thread(_render_process.bind(output_texture, texture_rids, blend_modes, clippings, alphas)) diff --git a/godot/src/compositor/paint_compositor.gd b/godot/src/compositor/paint_compositor.gd new file mode 100644 index 0000000..a8505ea --- /dev/null +++ b/godot/src/compositor/paint_compositor.gd @@ -0,0 +1,351 @@ +## レイヤーなどの合成を行うクラス。 +class_name PaintCompositor +extends Node + + +var _path_compositor: PathPaintCompositor = PathPaintCompositor.new() +var _fill_compositor: FillPaintCompositor = FillPaintCompositor.new() +var _group_compositor: GroupPaintCompositor = GroupPaintCompositor.new() +var _root_compositor: RootPaintCompositor = RootPaintCompositor.new() + + +var texture_size: Vector2i + +var _texture_pool: Array[TextureHandle] = [] + +var _create_texture_callables: Array[Callable] = [] + +class TextureHandle extends RefCounted: + var valid: bool = false + var texture: Texture2DRD + var texture_rid: RID + var texture_size: Vector2i + + +class MipTextureHandle extends RefCounted: + var valid: bool = false + var texture: Texture2DRD + var texture_rid: RID + var texture_size: Vector2i + var mipmap_texture_rids: Array[RID] = [] + var mip_size: int + + +var composite_textures: Dictionary = {} + +var root_composite_texture: MipTextureHandle = null + +var need_composite: bool = false + + +## テクスチャを一度に大量に生成するとvmaがエラーを起こしてテクスチャの生成に失敗するので、 +## 一度に大量にテクスチャを生成しないようにフレーム分散して生成する。 +func step_create_texture() -> void: + # 1フレーム中にテクスチャ生成は最大32個まで + if _create_texture_callables.size() > 0: + for i in 32: + if _create_texture_callables.size() == 0: + break + var callable: Callable = _create_texture_callables.pop_front() + RenderingServer.call_on_render_thread(callable) + + +## テクスチャをレンダースレッドで生成する。 +func _create_texture_in_render_thread(handle: TextureHandle, create_texture_size: Vector2i) -> void: + var rd := RenderingServer.get_rendering_device() + + var texture := Texture2DRD.new() + + var tf := RDTextureFormat.new() + tf.format = RenderingDevice.DATA_FORMAT_R8G8B8A8_UNORM + tf.texture_type = RenderingDevice.TEXTURE_TYPE_2D + tf.width = create_texture_size.x * 1 + tf.height = create_texture_size.y * 1 + tf.depth = 1 + tf.array_layers = 1 + tf.mipmaps = 1 + tf.usage_bits = RenderingDevice.TEXTURE_USAGE_SAMPLING_BIT + \ + RenderingDevice.TEXTURE_USAGE_COLOR_ATTACHMENT_BIT + \ + RenderingDevice.TEXTURE_USAGE_STORAGE_BIT + \ + RenderingDevice.TEXTURE_USAGE_CAN_UPDATE_BIT + \ + RenderingDevice.TEXTURE_USAGE_CAN_COPY_TO_BIT + \ + RenderingDevice.TEXTURE_USAGE_CAN_COPY_FROM_BIT + + var texture_rid := rd.texture_create(tf, RDTextureView.new(), []) + rd.texture_clear(texture_rid, Color(0, 0, 0, 0), 0, 1, 0, 1) + + texture.texture_rd_rid = texture_rid + + handle.texture = texture + handle.texture_rid = texture_rid + handle.texture_size = create_texture_size + handle.valid = true + + +## テクスチャをレンダースレッドで解放する。 +func _free_texture_in_render_thread(handle: TextureHandle) -> void: + handle.valid = false + var rd := RenderingServer.get_rendering_device() + rd.free_rid(handle.texture_rid) + + +## テクスチャをプールから取得する。 +func _get_texture_from_pool() -> TextureHandle: + if _texture_pool.size() > 1: + return _texture_pool.pop_back() + var texture_handle := TextureHandle.new() + _create_texture_callables.append(_create_texture_in_render_thread.bind(texture_handle, texture_size)) + return texture_handle + + +## テクスチャをプールに返却する。 +func _free_texture_to_pool(handle: TextureHandle) -> void: + if handle.texture_size == texture_size: + _texture_pool.append(handle) + else: + RenderingServer.call_on_render_thread(_free_texture_in_render_thread.bind(handle)) + + +## Mipテクスチャをレンダースレッドで生成する。 +func _create_mip_texture_in_render_thread(handle: MipTextureHandle, create_texture_size: Vector2i) -> void: + var rd := RenderingServer.get_rendering_device() + + var texture := Texture2DRD.new() + + var mip_size := int(log(maxf(create_texture_size.x, create_texture_size.y)) / log(2.0)) + 1 + + var tf := RDTextureFormat.new() + tf.format = RenderingDevice.DATA_FORMAT_R8G8B8A8_UNORM + tf.texture_type = RenderingDevice.TEXTURE_TYPE_2D + tf.width = create_texture_size.x * 1 + tf.height = create_texture_size.y * 1 + tf.depth = 1 + tf.array_layers = 1 + tf.mipmaps = mip_size + tf.usage_bits = RenderingDevice.TEXTURE_USAGE_SAMPLING_BIT + \ + RenderingDevice.TEXTURE_USAGE_COLOR_ATTACHMENT_BIT + \ + RenderingDevice.TEXTURE_USAGE_STORAGE_BIT + \ + RenderingDevice.TEXTURE_USAGE_CAN_UPDATE_BIT + \ + RenderingDevice.TEXTURE_USAGE_CAN_COPY_TO_BIT + \ + RenderingDevice.TEXTURE_USAGE_CAN_COPY_FROM_BIT + + var texture_rid := rd.texture_create(tf, RDTextureView.new(), []) + rd.texture_clear(texture_rid, Color(0, 0, 0, 0), 0, 1, 0, 1) + + texture.texture_rd_rid = texture_rid + + var mipmap_texture_rids: Array[RID] = [] + for i in mip_size: + mipmap_texture_rids.append(rd.texture_create_shared_from_slice(RDTextureView.new(), texture_rid, 0, i)) + + handle.texture = texture + handle.texture_rid = texture_rid + handle.texture_size = create_texture_size + handle.mipmap_texture_rids = mipmap_texture_rids + handle.mip_size = mip_size + handle.valid = true + + +## Mipテクスチャをレンダースレッドで解放する。 +func _free_mip_texture_in_render_thread(handle: MipTextureHandle) -> void: + handle.valid = false + var rd := RenderingServer.get_rendering_device() + for rid in handle.mipmap_texture_rids: + rd.free_rid(rid) + rd.free_rid(handle.texture_rid) + + +## Mipテクスチャをプールから取得する。 +func _create_mip_texture() -> MipTextureHandle: + var mip_texture_handle := MipTextureHandle.new() + RenderingServer.call_on_render_thread(_create_mip_texture_in_render_thread.bind(mip_texture_handle, texture_size)) + return mip_texture_handle + + +## Mipテクスチャをプールに返却する。 +func _free_mip_texture(handle: MipTextureHandle) -> void: + RenderingServer.call_on_render_thread(_free_mip_texture_in_render_thread.bind(handle)) + + +## ペイントレイヤーのcomposite_texturesをrootに合わせて生成破棄する。 +func update() -> void: + var ids: Array[String] = [] + for layer: PaintLayer in Main.layers.get_flatten_layers(): + ids.append(layer.id) + for id: String in composite_textures.keys(): + if not ids.has(id): + @warning_ignore("unsafe_cast") + var texture_handle := composite_textures[id] as TextureHandle + _free_texture_to_pool(texture_handle) + composite_textures.erase(id) + for id in ids: + if not composite_textures.has(id): + composite_textures[id] = _get_texture_from_pool() + + +## compositorをリサイズする。 +func resize(size: Vector2i) -> void: + _texture_pool.clear() + _path_compositor.resize(size) + _fill_compositor.resize(size) + _group_compositor.resize(size) + _root_compositor.resize(size) + texture_size = size + for id: String in composite_textures: + @warning_ignore("unsafe_cast") + var texture_handle := composite_textures[id] as TextureHandle + _free_texture_to_pool(texture_handle) + composite_textures[id] = _get_texture_from_pool() + if root_composite_texture != null: + _free_mip_texture(root_composite_texture) + root_composite_texture = _create_mip_texture() + + +## 子孫をたどってneed_compositeとneed_parent_compositeを見てmark_compositeを有効にする。 +func _update_mark_composite_group_layer(group_layer: GroupPaintLayer, force_composite: bool) -> bool: + for layer in group_layer.child_layers: + # テクスチャが確保されるまでcompositeをスキップする + if not composite_textures[layer.id].valid: + return false + if layer is GroupPaintLayer: + var gl := layer as GroupPaintLayer + if not _update_mark_composite_group_layer(gl, force_composite): + return false + if layer.need_parent_composite: + group_layer.need_composite = true + if layer.need_composite or force_composite: + group_layer.need_composite = true + layer.mark_composite = true + layer.need_parent_composite = false + layer.need_composite = false + return true + + +## パスレイヤーをcompositeする。 +func _composite_path_layer(path_layer: PathPaintLayer) -> void: + if path_layer.filled: + if path_layer.outlined: + var fill_material := Main.materials.get_material(path_layer.fill_material_id) + var line_material := Main.materials.get_material(path_layer.outline_material_id) + @warning_ignore("unsafe_cast") + var texture_handle := composite_textures[path_layer.id] as TextureHandle + _path_compositor.path_composite_jfa_line(texture_handle, path_layer, fill_material, line_material, path_layer.outline_width) + else: + var fill_material := Main.materials.get_material(path_layer.fill_material_id) + @warning_ignore("unsafe_cast") + var texture_handle := composite_textures[path_layer.id] as TextureHandle + _path_compositor.path_composite(texture_handle, path_layer, fill_material) + elif path_layer.outlined: + var line_material := Main.materials.get_material(path_layer.outline_material_id) + @warning_ignore("unsafe_cast") + var texture_handle := composite_textures[path_layer.id] as TextureHandle + _path_compositor.path_composite(texture_handle, path_layer, line_material) + else: + @warning_ignore("unsafe_cast") + var texture_handle := composite_textures[path_layer.id] as TextureHandle + _path_compositor.path_clear(texture_handle) + + +## 塗りレイヤーをcompositeする。 +func _composite_fill_layer(fill_layer: FillPaintLayer) -> void: + var fill_material := Main.materials.get_material(fill_layer.fill_material_id) + @warning_ignore("unsafe_cast") + var texture_handle := composite_textures[fill_layer.id] as TextureHandle + _fill_compositor.composite(texture_handle, fill_material) + + +## グループレイヤーをcompositeする。 +func _composite_group_layer(group_layer: GroupPaintLayer) -> void: + # 子要素のcompositeを更新 + for layer in group_layer.child_layers: + if layer.mark_composite: + if layer is PathPaintLayer: + var pl := layer as PathPaintLayer + _composite_path_layer(pl) + elif layer is FillPaintLayer: + var fl := layer as FillPaintLayer + _composite_fill_layer(fl) + elif layer is GroupPaintLayer: + var gl := layer as GroupPaintLayer + _composite_group_layer(gl) + layer.mark_composite = false + + # compositeする + var texture_rids: Array[RID] = [] + var blend_modes: Array[PaintLayer.BlendMode] = [] + var clippings: Array[bool] = [] + var alphas: Array[int] = [] + for layer in group_layer.child_layers: + if not layer.visible: + continue + texture_rids.append(composite_textures[layer.id].texture_rid) + blend_modes.append(layer.blend_mode) + clippings.append(layer.clipped) + alphas.append(layer.alpha) + @warning_ignore("unsafe_cast") + var texture_handle := composite_textures[group_layer.id] as TextureHandle + _group_compositor.composite(texture_handle, texture_rids, blend_modes, clippings, alphas) + + +## rootからたどって必要な部分をcompositeし直す。 +## テクスチャの生成がなされていない場合など、compositeができない場合はfalseを返す。 +func composite(root: Array[PaintLayer], force_composite: bool = false) -> bool: + # テクスチャが確保されるまでcompositeをスキップする + if not root_composite_texture.valid: + return false + + # rootの子孫をたどってneed_compositeとneed_parent_compositeを見てmark_compositeを有効にする + var mark_composite_root := force_composite or need_composite + for layer in root: + if layer is GroupPaintLayer: + var gl := layer as GroupPaintLayer + if not _update_mark_composite_group_layer(gl, force_composite): + return false + + # テクスチャが確保されるまでcompositeをスキップする + if not composite_textures[layer.id].valid: + return false + + if layer.need_parent_composite: + mark_composite_root = true + if layer.need_composite or force_composite: + mark_composite_root = true + layer.mark_composite = true + layer.need_parent_composite = false + layer.need_composite = false + if need_composite: + mark_composite_root = true + need_composite = false + + if not mark_composite_root: + return true + + # mark_compositeな部分だけ深さ優先でcompositeしていく + for layer in root: + if layer.mark_composite: + if layer is PathPaintLayer: + var pl := layer as PathPaintLayer + _composite_path_layer(pl) + elif layer is FillPaintLayer: + var fl := layer as FillPaintLayer + _composite_fill_layer(fl) + elif layer is GroupPaintLayer: + var gl := layer as GroupPaintLayer + _composite_group_layer(gl) + layer.mark_composite = false + + # compositeする + var texture_rids: Array[RID] = [] + var blend_modes: Array[PaintLayer.BlendMode] = [] + var clippings: Array[bool] = [] + var alphas: Array[int] = [] + for layer in root: + if not layer.visible: + continue + texture_rids.append(composite_textures[layer.id].texture_rid) + blend_modes.append(layer.blend_mode) + clippings.append(layer.clipped) + alphas.append(layer.alpha) + _root_compositor.composite(root_composite_texture, texture_rids, blend_modes, clippings, alphas) + + return true diff --git a/godot/src/compositor/path_paint_compositor.gd b/godot/src/compositor/path_paint_compositor.gd new file mode 100644 index 0000000..f9b8ed3 --- /dev/null +++ b/godot/src/compositor/path_paint_compositor.gd @@ -0,0 +1,1037 @@ +## booleanのパスのviewportの結果を解決して塗りを与えるcompositor。 +class_name PathPaintCompositor +extends RefCounted + + +var texture: Texture2DRD + +var texture_size: Vector2i + +## ラスタライズのグラフィクスシェーダー。 +var _rasterize_render_shader: RID +## ラスタライズのグラフィクスパイプライン。 +var _rasterize_render_pipeline: RID +## バーテックスバッファのフォーマット。 +var _rasterize_vertex_format: int +## ラスタライズ結果を保存するテクスチャ。 +var _rasterize_texture_rids: Array[RID] +## ラスタライズ結果を保存するテクスチャのフレームバッファ。 +var _rasterize_framebuffers: Array[RID] +## フレームバッファのフォーマット。 +var _rasterize_framebuffer_format: int + +## 塗り合成コンピュートシェーダー。 +var _path_composite_shader: RID +## 塗り合成コンピュートパイプライン。 +var _path_composite_pipeline: RID + +## サンプラー。 +var _sampler: RID + +## Jump Flood Algorithm初期化のコンピュートシェーダー。 +var _init_jfa_line_shader: RID +## Jump Flood Algorithm初期化のコンピュートパイプライン。 +var _init_jfa_line_pipeline: RID + +## Jump Flood Algorithmのコンピュートシェーダー。 +var _jfa_line_shader: RID +## Jump Flood Algorithmのコンピュートパイプライン。 +var _jfa_line_pipeline: RID + +## ライン付きの塗り合成コンピュートシェーダー。 +var _path_composite_jfa_line_shader: RID +## ライン付きの塗り合成コンピュートパイプライン。 +var _path_composite_jfa_line_pipeline: RID + +## Jump Flood Algorithmに使うテクスチャとそのUniformのバインドセット。 +var _jfa_texture_rids: Array[RID] = [] +var _init_jfa_texture_set: RID +var _jfa_texture_sets: Array[RID] = [] +var _path_composite_jfa_line_texture_sets: Array[RID] + +## 内部で結果を一時的に描画するバッファ。 +var _internal_texture_rid: RID + +## スケールダウンのコンピュートシェーダー。 +var _scale_down_shader: RID +## スケールダウンのコンピュートパイプライン。 +var _scale_down_pipeline: RID + + +func _init() -> void: + texture = Texture2DRD.new() + RenderingServer.call_on_render_thread(_initialize_compositor) + + +func _notification(what: int) -> void: + if what == NOTIFICATION_PREDELETE: + var free_compute_resource := func( + rasterize_render_shader: RID, + path_composite_shader: RID, + sampler: RID, + init_jfa_line_shader: RID, + jfa_line_shader: RID, + path_composite_jfa_line_shader: RID, + jfa_texture_rids: Array[RID] + ) -> void: + var rd := RenderingServer.get_rendering_device() + if rasterize_render_shader: + rd.free_rid(rasterize_render_shader) + if path_composite_shader: + rd.free_rid(path_composite_shader) + if sampler: + rd.free_rid(sampler) + if init_jfa_line_shader: + rd.free_rid(init_jfa_line_shader) + if jfa_line_shader: + rd.free_rid(jfa_line_shader) + if path_composite_jfa_line_shader: + rd.free_rid(path_composite_jfa_line_shader) + for rid in jfa_texture_rids: + rd.free_rid(rid) + RenderingServer.call_on_render_thread(free_compute_resource.bind( + _rasterize_render_shader, _path_composite_shader, _sampler, + _init_jfa_line_shader, _jfa_line_shader, _path_composite_jfa_line_shader, _jfa_texture_rids)) + + +func _initialize_compositor() -> void: + var rd := RenderingServer.get_rendering_device() + + var attachments: Array[RDAttachmentFormat] = [] + var attachment_format := RDAttachmentFormat.new() + attachment_format.set_format(RenderingDevice.DATA_FORMAT_R8G8_SNORM) + attachment_format.set_samples(RenderingDevice.TEXTURE_SAMPLES_1) + attachment_format.usage_flags = RenderingDevice.TEXTURE_USAGE_SAMPLING_BIT + \ + RenderingDevice.TEXTURE_USAGE_COLOR_ATTACHMENT_BIT + \ + RenderingDevice.TEXTURE_USAGE_STORAGE_BIT + \ + RenderingDevice.TEXTURE_USAGE_CAN_UPDATE_BIT + \ + RenderingDevice.TEXTURE_USAGE_CAN_COPY_TO_BIT + \ + RenderingDevice.TEXTURE_USAGE_CAN_COPY_FROM_BIT + attachments.push_back(attachment_format) + _rasterize_framebuffer_format = rd.framebuffer_format_create(attachments) + + var vertex_attrs := [RDVertexAttribute.new()] + vertex_attrs[0].format = RenderingDevice.DATA_FORMAT_R32G32_SFLOAT + vertex_attrs[0].location = 0 + vertex_attrs[0].stride = 4 * 2 + _rasterize_vertex_format = rd.vertex_format_create(vertex_attrs) + + var blend := RDPipelineColorBlendState.new() + var blend_attachment := RDPipelineColorBlendStateAttachment.new() + blend_attachment.write_r = true + blend_attachment.write_g = true + blend_attachment.write_b = true + blend_attachment.enable_blend = false + blend.attachments.push_back(blend_attachment) + + var rasterize_render_shader_file: RDShaderFile = load("res://shaders/composite/rasterize.glsl") + var rasterize_render_shader_spirv := rasterize_render_shader_file.get_spirv() + _rasterize_render_shader = rd.shader_create_from_spirv(rasterize_render_shader_spirv) + _rasterize_render_pipeline = rd.render_pipeline_create( + _rasterize_render_shader, + _rasterize_framebuffer_format, + _rasterize_vertex_format, + RenderingDevice.RENDER_PRIMITIVE_TRIANGLES, + RDPipelineRasterizationState.new(), + RDPipelineMultisampleState.new(), + RDPipelineDepthStencilState.new(), + blend) + + var path_composite_shader_file: RDShaderFile = load("res://shaders/composite/path_composite.glsl") + var path_composite_shader_spirv := path_composite_shader_file.get_spirv() + _path_composite_shader = rd.shader_create_from_spirv(path_composite_shader_spirv) + _path_composite_pipeline = rd.compute_pipeline_create(_path_composite_shader) + + var sampler_state := RDSamplerState.new() + sampler_state.min_filter = RenderingDevice.SAMPLER_FILTER_LINEAR + sampler_state.mag_filter = RenderingDevice.SAMPLER_FILTER_LINEAR + sampler_state.mip_filter = RenderingDevice.SAMPLER_FILTER_LINEAR + _sampler = rd.sampler_create(sampler_state) + + var init_jfa_line_shader_file: RDShaderFile = load("res://shaders/composite/init_jfa_line.glsl") + var init_jfa_line_shader_spirv := init_jfa_line_shader_file.get_spirv() + _init_jfa_line_shader = rd.shader_create_from_spirv(init_jfa_line_shader_spirv) + _init_jfa_line_pipeline = rd.compute_pipeline_create(_init_jfa_line_shader) + + var jfa_line_shader_file: RDShaderFile = load("res://shaders/composite/jfa_line.glsl") + var jfa_line_shader_spirv := jfa_line_shader_file.get_spirv() + _jfa_line_shader = rd.shader_create_from_spirv(jfa_line_shader_spirv) + _jfa_line_pipeline = rd.compute_pipeline_create(_jfa_line_shader) + + var path_composite_jfa_line_shader_file: RDShaderFile = load("res://shaders/composite/path_composite_jfa_line.glsl") + var path_composite_jfa_line_shader_spirv := path_composite_jfa_line_shader_file.get_spirv() + _path_composite_jfa_line_shader = rd.shader_create_from_spirv(path_composite_jfa_line_shader_spirv) + _path_composite_jfa_line_pipeline = rd.compute_pipeline_create(_path_composite_jfa_line_shader) + + var scale_down_shader_file: RDShaderFile = load("res://shaders/composite/mipmap_generate.glsl") + var scale_down_shader_spirv := scale_down_shader_file.get_spirv() + _scale_down_shader = rd.shader_create_from_spirv(scale_down_shader_spirv) + _scale_down_pipeline = rd.compute_pipeline_create(_scale_down_shader) + + +func _recreate_texture(size: Vector2i) -> void: + var rd := RenderingServer.get_rendering_device() + + texture_size = size + + var rasterize_tf := RDTextureFormat.new() + rasterize_tf.format = RenderingDevice.DATA_FORMAT_R8G8_SNORM + rasterize_tf.texture_type = RenderingDevice.TEXTURE_TYPE_2D + rasterize_tf.width = texture_size.x * 2 + rasterize_tf.height = texture_size.y * 2 + rasterize_tf.depth = 1 + rasterize_tf.array_layers = 1 + rasterize_tf.mipmaps = 1 + rasterize_tf.usage_bits = RenderingDevice.TEXTURE_USAGE_SAMPLING_BIT + \ + RenderingDevice.TEXTURE_USAGE_COLOR_ATTACHMENT_BIT + \ + RenderingDevice.TEXTURE_USAGE_STORAGE_BIT + \ + RenderingDevice.TEXTURE_USAGE_CAN_UPDATE_BIT + \ + RenderingDevice.TEXTURE_USAGE_CAN_COPY_TO_BIT + \ + RenderingDevice.TEXTURE_USAGE_CAN_COPY_FROM_BIT + _rasterize_texture_rids = [ + rd.texture_create(rasterize_tf, RDTextureView.new()), + rd.texture_create(rasterize_tf, RDTextureView.new()), + ] + _rasterize_framebuffers = [ + rd.framebuffer_create([_rasterize_texture_rids[0]], _rasterize_framebuffer_format), + rd.framebuffer_create([_rasterize_texture_rids[1]], _rasterize_framebuffer_format), + ] + + _jfa_texture_rids.clear() + _jfa_texture_sets.clear() + _path_composite_jfa_line_texture_sets.clear() + + var jfa_tf := RDTextureFormat.new() + jfa_tf.format = RenderingDevice.DATA_FORMAT_R16G16B16A16_SFLOAT + jfa_tf.texture_type = RenderingDevice.TEXTURE_TYPE_2D + jfa_tf.width = texture_size.x * 2 + jfa_tf.height = texture_size.y * 2 + jfa_tf.depth = 1 + jfa_tf.array_layers = 1 + jfa_tf.mipmaps = 1 + jfa_tf.usage_bits = RenderingDevice.TEXTURE_USAGE_STORAGE_BIT + + for idx in 2: + var texture_rid := rd.texture_create(jfa_tf, RDTextureView.new(), []) + _jfa_texture_rids.append(texture_rid) + + var init_jfa_uniform := RDUniform.new() + init_jfa_uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_IMAGE + init_jfa_uniform.binding = 0 + init_jfa_uniform.add_id(_jfa_texture_rids[0]) + _init_jfa_texture_set = rd.uniform_set_create([init_jfa_uniform], _init_jfa_line_shader, 0) + + for idx in 2: + var jfa_uniform := RDUniform.new() + jfa_uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_IMAGE + jfa_uniform.binding = 0 + jfa_uniform.add_id(_jfa_texture_rids[idx]) + var texture_set := rd.uniform_set_create([jfa_uniform], _jfa_line_shader, 0) + _jfa_texture_sets.append(texture_set) + + for idx in 2: + var path_composite_jfa_uniform := RDUniform.new() + path_composite_jfa_uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_IMAGE + path_composite_jfa_uniform.binding = 0 + path_composite_jfa_uniform.add_id(_jfa_texture_rids[idx]) + var texture_set := rd.uniform_set_create([path_composite_jfa_uniform], _path_composite_jfa_line_shader, 0) + _path_composite_jfa_line_texture_sets.append(texture_set) + + var tf := RDTextureFormat.new() + tf.format = RenderingDevice.DATA_FORMAT_R8G8B8A8_UNORM + tf.texture_type = RenderingDevice.TEXTURE_TYPE_2D + tf.width = texture_size.x * 2 + tf.height = texture_size.y * 2 + tf.depth = 1 + tf.array_layers = 1 + tf.mipmaps = 6 + tf.usage_bits = RenderingDevice.TEXTURE_USAGE_SAMPLING_BIT + \ + RenderingDevice.TEXTURE_USAGE_COLOR_ATTACHMENT_BIT + \ + RenderingDevice.TEXTURE_USAGE_STORAGE_BIT + \ + RenderingDevice.TEXTURE_USAGE_CAN_UPDATE_BIT + \ + RenderingDevice.TEXTURE_USAGE_CAN_COPY_TO_BIT + \ + RenderingDevice.TEXTURE_USAGE_CAN_COPY_FROM_BIT + + _internal_texture_rid = rd.texture_create(tf, RDTextureView.new(), []) + rd.texture_clear(_internal_texture_rid, Color(0, 0, 0, 0), 0, 1, 0, 1) + + +func resize(size: Vector2i) -> void: + if texture_size != size: + RenderingServer.call_on_render_thread(_recreate_texture.bind(size)) + + +func _path_clear_render_process(composite_texture: PaintCompositor.TextureHandle) -> void: + var rd := RenderingServer.get_rendering_device() + rd.texture_clear(composite_texture.texture_rid, Color(0, 0, 0, 0), 0, 1, 0, 1, ) + + +func path_clear(composite_texture: PaintCompositor.TextureHandle) -> void: + RenderingServer.call_on_render_thread(_path_clear_render_process.bind(composite_texture)) + + +func _path_composite_render_process(composite_texture: PaintCompositor.TextureHandle, path_layer: PathPaintLayer, material: PaintMaterial) -> void: + var rd := RenderingServer.get_rendering_device() + + if not rd.texture_is_valid(composite_texture.texture_rid): + return + + var x_group := (texture_size.x * 2 - 1) / 8 + 1 + var y_group := (texture_size.y * 2 - 1) / 8 + 1 + + # === ラスタライズ === + + rd.texture_clear(_rasterize_texture_rids[0], Color(0, 0, 0, 0), 0, 1, 0, 1) + rd.texture_clear(_rasterize_texture_rids[1], Color(0, 0, 0, 0), 0, 1, 0, 1) + var framebuffer_index := 0 + + if path_layer.filled: + + var clear_color_values := PackedColorArray([Color(0, 0, 0, 0)]) + var draw_list := rd.draw_list_begin( + _rasterize_framebuffers[framebuffer_index], + RenderingDevice.INITIAL_ACTION_KEEP, + RenderingDevice.FINAL_ACTION_READ, + RenderingDevice.INITIAL_ACTION_KEEP, + RenderingDevice.FINAL_ACTION_READ, + clear_color_values) + rd.draw_list_bind_render_pipeline(draw_list, _rasterize_render_pipeline) + + for path in path_layer.paths: + if not path.visible: + continue + if path.indices.size() <= 2: + continue + + if path.boolean == Path.Boolean.Intersect or path.boolean == Path.Boolean.Xor: + rd.draw_list_end() + framebuffer_index = 1 - framebuffer_index + rd.texture_copy( + _rasterize_texture_rids[1 - framebuffer_index], + _rasterize_texture_rids[framebuffer_index], + Vector3(0, 0, 0), + Vector3(0, 0, 0), + Vector3(texture_size.x * 2, texture_size.y * 2, 0), + 0, 0, 0, 0) + draw_list = rd.draw_list_begin( + _rasterize_framebuffers[framebuffer_index], + RenderingDevice.INITIAL_ACTION_KEEP, + RenderingDevice.FINAL_ACTION_READ, + RenderingDevice.INITIAL_ACTION_KEEP, + RenderingDevice.FINAL_ACTION_READ, + clear_color_values) + rd.draw_list_bind_render_pipeline(draw_list, _rasterize_render_pipeline) + + # index bufferの作成 + var index_bytes := path.indices.to_byte_array() + var index_buffer := rd.index_buffer_create(path.indices.size(), RenderingDevice.INDEX_BUFFER_FORMAT_UINT32, index_bytes) + var index_array := rd.index_array_create(index_buffer, 0, path.indices.size()) + + # vertex bufferの作成 + var vertex_bytes := path.vertices.to_byte_array() + var vertex_buffers := [rd.vertex_buffer_create(vertex_bytes.size(), vertex_bytes)] + var vertex_array := rd.vertex_array_create(path.vertices.size(), _rasterize_vertex_format, vertex_buffers) + + # framebufferのuniformのバインド + var framebuffer_texture_uniform := RDUniform.new() + framebuffer_texture_uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_SAMPLER_WITH_TEXTURE + framebuffer_texture_uniform.binding = 0 + framebuffer_texture_uniform.add_id(_sampler) + framebuffer_texture_uniform.add_id(_rasterize_texture_rids[1 - framebuffer_index]) + var framebuffer_texture_set := rd.uniform_set_create([framebuffer_texture_uniform], _rasterize_render_shader, 0) + + # push_counstantsを詰める + var rasterize_push_constant := PackedInt32Array() + rasterize_push_constant.push_back(int(Main.document_size.x)) + rasterize_push_constant.push_back(int(Main.document_size.y)) + if path.boolean == Path.Boolean.Union: + rasterize_push_constant.push_back(0) + elif path.boolean == Path.Boolean.Diff: + rasterize_push_constant.push_back(1) + elif path.boolean == Path.Boolean.Intersect: + rasterize_push_constant.push_back(2) + elif path.boolean == Path.Boolean.Xor: + rasterize_push_constant.push_back(3) + rasterize_push_constant.push_back(0) # dummy + var rasterize_push_constant_bytes := rasterize_push_constant.to_byte_array() + + rd.draw_list_bind_index_array(draw_list, index_array) + rd.draw_list_bind_vertex_array(draw_list, vertex_array) + rd.draw_list_bind_uniform_set(draw_list, framebuffer_texture_set, 0) + rd.draw_list_set_push_constant(draw_list, rasterize_push_constant_bytes, rasterize_push_constant_bytes.size()) + rd.draw_list_draw(draw_list, true, 1) + + # intersectの2つ目のパス + if path.boolean == Path.Boolean.Intersect: + rd.draw_list_end() + framebuffer_index = 1 - framebuffer_index + rd.texture_copy( + _rasterize_texture_rids[1 - framebuffer_index], + _rasterize_texture_rids[framebuffer_index], + Vector3(0, 0, 0), + Vector3(0, 0, 0), + Vector3(texture_size.x * 2, texture_size.y * 2, 0), + 0, 0, 0, 0) + draw_list = rd.draw_list_begin( + _rasterize_framebuffers[framebuffer_index], + RenderingDevice.INITIAL_ACTION_KEEP, + RenderingDevice.FINAL_ACTION_READ, + RenderingDevice.INITIAL_ACTION_KEEP, + RenderingDevice.FINAL_ACTION_READ, + clear_color_values) + rd.draw_list_bind_render_pipeline(draw_list, _rasterize_render_pipeline) + + # vertex bufferの作成 + var rect_vertices := PackedVector2Array([ + Vector2(0, 0), + Vector2(texture_size.x * 2, 0), + Vector2(texture_size.x * 2, texture_size.y * 2), + Vector2(0, 0), + Vector2(texture_size.x * 2, texture_size.y * 2), + Vector2(0, texture_size.y * 2), + ]) + var rect_vertex_bytes := rect_vertices.to_byte_array() + var rect_vertex_buffers := [rd.vertex_buffer_create(rect_vertex_bytes.size(), rect_vertex_bytes)] + var rect_vertex_array := rd.vertex_array_create(6, _rasterize_vertex_format, rect_vertex_buffers) + + # framebufferのuniformのバインド + var rect_framebuffer_texture_uniform := RDUniform.new() + rect_framebuffer_texture_uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_SAMPLER_WITH_TEXTURE + rect_framebuffer_texture_uniform.binding = 0 + rect_framebuffer_texture_uniform.add_id(_sampler) + rect_framebuffer_texture_uniform.add_id(_rasterize_texture_rids[1 - framebuffer_index]) + var rect_framebuffer_texture_set := rd.uniform_set_create([rect_framebuffer_texture_uniform], _rasterize_render_shader, 0) + + # push_counstantsを詰める + var rect_rasterize_push_constant := PackedInt32Array() + rect_rasterize_push_constant.push_back(texture_size.x * 2) + rect_rasterize_push_constant.push_back(texture_size.y * 2) + rect_rasterize_push_constant.push_back(5) + rect_rasterize_push_constant.push_back(0) # dummy + var rect_rasterize_push_constant_bytes := rect_rasterize_push_constant.to_byte_array() + + rd.draw_list_bind_vertex_array(draw_list, rect_vertex_array) + rd.draw_list_bind_uniform_set(draw_list, rect_framebuffer_texture_set, 0) + rd.draw_list_set_push_constant(draw_list, rect_rasterize_push_constant_bytes, rect_rasterize_push_constant_bytes.size()) + rd.draw_list_draw(draw_list, true, 1) + + rd.draw_list_end() + else: + var clear_color_values := PackedColorArray([Color(0, 0, 0, 0)]) + var draw_list := rd.draw_list_begin( + _rasterize_framebuffers[framebuffer_index], + RenderingDevice.INITIAL_ACTION_KEEP, + RenderingDevice.FINAL_ACTION_READ, + RenderingDevice.INITIAL_ACTION_KEEP, + RenderingDevice.FINAL_ACTION_READ, + clear_color_values) + rd.draw_list_bind_render_pipeline(draw_list, _rasterize_render_pipeline) + + for path in path_layer.paths: + if not path.visible: + continue + if path.line_indices.size() <= 2: + continue + + # index bufferの作成 + var index_bytes := path.line_indices.to_byte_array() + var index_buffer := rd.index_buffer_create(path.line_indices.size(), RenderingDevice.INDEX_BUFFER_FORMAT_UINT32, index_bytes) + var index_array := rd.index_array_create(index_buffer, 0, path.line_indices.size()) + + # vertex bufferの作成 + var vertex_bytes := path.line_vertices.to_byte_array() + var vertex_buffers := [rd.vertex_buffer_create(vertex_bytes.size(), vertex_bytes)] + var vertex_array := rd.vertex_array_create(path.line_vertices.size(), _rasterize_vertex_format, vertex_buffers) + + # framebufferのuniformのバインド + var framebuffer_texture_uniform := RDUniform.new() + framebuffer_texture_uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_SAMPLER_WITH_TEXTURE + framebuffer_texture_uniform.binding = 0 + framebuffer_texture_uniform.add_id(_sampler) + framebuffer_texture_uniform.add_id(_rasterize_texture_rids[1 - framebuffer_index]) + var framebuffer_texture_set := rd.uniform_set_create([framebuffer_texture_uniform], _rasterize_render_shader, 0) + + # push_counstantsを詰める + var rasterize_push_constant := PackedInt32Array() + rasterize_push_constant.push_back(int(Main.document_size.x)) + rasterize_push_constant.push_back(int(Main.document_size.y)) + rasterize_push_constant.push_back(0) + rasterize_push_constant.push_back(0) # dummy + var rasterize_push_constant_bytes := rasterize_push_constant.to_byte_array() + + rd.draw_list_bind_index_array(draw_list, index_array) + rd.draw_list_bind_vertex_array(draw_list, vertex_array) + rd.draw_list_bind_uniform_set(draw_list, framebuffer_texture_set, 0) + rd.draw_list_set_push_constant(draw_list, rasterize_push_constant_bytes, rasterize_push_constant_bytes.size()) + rd.draw_list_draw(draw_list, true, 1) + + rd.draw_list_end() + + # === ラスタライズの結果をもとにcompositeする === + + # 出力画像のuniform + var internal_texture_uniform := RDUniform.new() + internal_texture_uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_IMAGE + internal_texture_uniform.binding = 0 + internal_texture_uniform.add_id(_internal_texture_rid) + var internal_texture_set := rd.uniform_set_create([internal_texture_uniform], _path_composite_shader, 0) + + # rasterize画像のuniform + var rasterize_uniform := RDUniform.new() + rasterize_uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_SAMPLER_WITH_TEXTURE + rasterize_uniform.binding = 0 + rasterize_uniform.add_id(_sampler) + rasterize_uniform.add_id(_rasterize_texture_rids[framebuffer_index]) + var rasterize_uniform_set := rd.uniform_set_create([rasterize_uniform], _path_composite_shader, 1) + + # グラデーションのテクスチャのuniform + var gradient_texture_uniform := RDUniform.new() + gradient_texture_uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_SAMPLER_WITH_TEXTURE + gradient_texture_uniform.binding = 0 + gradient_texture_uniform.add_id(_sampler) + if material is ColorPaintMaterial: + gradient_texture_uniform.add_id(RenderingServer.texture_get_rd_texture(RenderingServer.get_white_texture())) + elif material is LinearGradientPaintMaterial: + var linear_gradient_material := material as LinearGradientPaintMaterial + var gradient_rid := RenderingServer.texture_get_rd_texture(linear_gradient_material.gradient_texture) + gradient_texture_uniform.add_id(gradient_rid) + elif material is RadialGradientPaintMaterial: + var radial_gradient_material := material as RadialGradientPaintMaterial + var gradient_rid := RenderingServer.texture_get_rd_texture(radial_gradient_material.gradient_texture) + gradient_texture_uniform.add_id(gradient_rid) + elif material == null: + gradient_texture_uniform.add_id(RenderingServer.texture_get_rd_texture(RenderingServer.get_white_texture())) + var gradient_texture_set := rd.uniform_set_create([gradient_texture_uniform], _path_composite_shader, 2) + + # push_counstantsを詰める + var push_constant := PackedInt32Array() + push_constant.push_back(texture_size.x * 2) + push_constant.push_back(texture_size.y * 2) + + if material is ColorPaintMaterial: + var color_material := material as ColorPaintMaterial + push_constant.push_back(0) # material type + push_constant.push_back(color_material.color.r8) + push_constant.push_back(color_material.color.g8) + push_constant.push_back(color_material.color.b8) + push_constant.push_back(color_material.color.a8) + push_constant.push_back(0) # dummmy + var pos_bytes := PackedVector2Array([ + Vector2(0, 0), + Vector2(0, 0), + Vector2(0, 0), + ]).to_byte_array().to_int32_array() + push_constant.append_array(pos_bytes) + elif material is LinearGradientPaintMaterial: + var linear_gradient_material := material as LinearGradientPaintMaterial + push_constant.push_back(1) # material type + push_constant.push_back(0) + push_constant.push_back(0) + push_constant.push_back(0) + push_constant.push_back(0) + push_constant.push_back(0) # dummmy + var pos_bytes := PackedVector2Array([ + linear_gradient_material.start_point, + linear_gradient_material.end_point, + Vector2(0, 0), + ]).to_byte_array().to_int32_array() + push_constant.append_array(pos_bytes) + elif material is RadialGradientPaintMaterial: + var radial_gradient_material := material as RadialGradientPaintMaterial + push_constant.push_back(2) # material type + push_constant.push_back(0) + push_constant.push_back(0) + push_constant.push_back(0) + push_constant.push_back(0) + push_constant.push_back(0) # dummmy + var pos_bytes := PackedVector2Array([ + radial_gradient_material.center_point, + radial_gradient_material.handle_1_point, + radial_gradient_material.handle_2_point, + ]).to_byte_array().to_int32_array() + push_constant.append_array(pos_bytes) + elif material == null: + push_constant.push_back(3) # material type + push_constant.push_back(0) + push_constant.push_back(0) + push_constant.push_back(0) + push_constant.push_back(0) + push_constant.push_back(0) # dummmy + var pos_bytes := PackedVector2Array([ + Vector2(0, 0), + Vector2(0, 0), + Vector2(0, 0), + ]).to_byte_array().to_int32_array() + push_constant.append_array(pos_bytes) + + push_constant.push_back(0) # pading + push_constant.push_back(0) # pading + + # コマンドを発行 + var compute_list := rd.compute_list_begin() + rd.compute_list_bind_compute_pipeline(compute_list, _path_composite_pipeline) + rd.compute_list_bind_uniform_set(compute_list, internal_texture_set, 0) + rd.compute_list_bind_uniform_set(compute_list, rasterize_uniform_set, 1) + rd.compute_list_bind_uniform_set(compute_list, gradient_texture_set, 2) + rd.compute_list_set_push_constant(compute_list, push_constant.to_byte_array(), push_constant.size() * 4) + rd.compute_list_dispatch(compute_list, x_group, y_group, 1) + rd.compute_list_end() + + # === 縮小して合成結果を書き出す === + + var x_group_scale_down := (texture_size.x - 1) / 8 + 1 + var y_group_scale_down := (texture_size.y - 1) / 8 + 1 + + # 出力画像のuniform + var texture_uniform := RDUniform.new() + texture_uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_IMAGE + texture_uniform.binding = 0 + texture_uniform.add_id(composite_texture.texture_rid) + var texture_set := rd.uniform_set_create([texture_uniform], _scale_down_shader, 0) + + # 入力画像のuniform + var scale_down_baseLtexture_uniform := RDUniform.new() + scale_down_baseLtexture_uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_SAMPLER_WITH_TEXTURE + scale_down_baseLtexture_uniform.binding = 0 + scale_down_baseLtexture_uniform.add_id(_sampler) + scale_down_baseLtexture_uniform.add_id(_internal_texture_rid) + var scale_down_baseLtexture_set := rd.uniform_set_create([scale_down_baseLtexture_uniform], _scale_down_shader, 1) + + # push_counstantsを詰める + var scale_down_push_constant := PackedInt32Array() + scale_down_push_constant.push_back(texture_size.x * 1) + scale_down_push_constant.push_back(texture_size.y * 1) + scale_down_push_constant.push_back(0) # padding + scale_down_push_constant.push_back(0) # padding + + # コマンドを発行 + var scale_down_compute_list := rd.compute_list_begin() + rd.compute_list_bind_compute_pipeline(scale_down_compute_list, _scale_down_pipeline) + rd.compute_list_bind_uniform_set(scale_down_compute_list, texture_set, 0) + rd.compute_list_bind_uniform_set(scale_down_compute_list, scale_down_baseLtexture_set, 1) + rd.compute_list_set_push_constant(scale_down_compute_list, scale_down_push_constant.to_byte_array(), scale_down_push_constant.size() * 4) + rd.compute_list_dispatch(scale_down_compute_list, x_group_scale_down, y_group_scale_down, 1) + rd.compute_list_end() + + +func path_composite(composite_texture: PaintCompositor.TextureHandle, path_layer: PathPaintLayer, material: PaintMaterial) -> void: + RenderingServer.call_on_render_thread(_path_composite_render_process.bind(composite_texture, path_layer, material)) + + +func _path_composite_jfa_line_render_process(composite_texture: PaintCompositor.TextureHandle, path_layer: PathPaintLayer, fill_material: PaintMaterial, line_material: PaintMaterial, line_width: float) -> void: + var rd := RenderingServer.get_rendering_device() + + if not rd.texture_is_valid(composite_texture.texture_rid): + return + + var x_group := (texture_size.x * 2 - 1) / 8 + 1 + var y_group := (texture_size.y * 2 - 1) / 8 + 1 + + # === ラスタライズ === + + rd.texture_clear(_rasterize_texture_rids[0], Color(0, 0, 0, 0), 0, 1, 0, 1) + rd.texture_clear(_rasterize_texture_rids[1], Color(0, 0, 0, 0), 0, 1, 0, 1) + var framebuffer_index := 0 + + var clear_color_values := PackedColorArray([Color(0, 0, 0, 0)]) + var draw_list := rd.draw_list_begin( + _rasterize_framebuffers[framebuffer_index], + RenderingDevice.INITIAL_ACTION_KEEP, + RenderingDevice.FINAL_ACTION_READ, + RenderingDevice.INITIAL_ACTION_KEEP, + RenderingDevice.FINAL_ACTION_READ, + clear_color_values) + rd.draw_list_bind_render_pipeline(draw_list, _rasterize_render_pipeline) + + for path in path_layer.paths: + if not path.visible: + continue + if path.indices.size() <= 2: + continue + + # backbufferとの交換 + if path.boolean == Path.Boolean.Intersect or path.boolean == Path.Boolean.Xor: + rd.draw_list_end() + framebuffer_index = 1 - framebuffer_index + rd.texture_copy( + _rasterize_texture_rids[1 - framebuffer_index], + _rasterize_texture_rids[framebuffer_index], + Vector3(0, 0, 0), + Vector3(0, 0, 0), + Vector3(texture_size.x * 2, texture_size.y * 2, 0), + 0, 0, 0, 0) + draw_list = rd.draw_list_begin( + _rasterize_framebuffers[framebuffer_index], + RenderingDevice.INITIAL_ACTION_KEEP, + RenderingDevice.FINAL_ACTION_READ, + RenderingDevice.INITIAL_ACTION_KEEP, + RenderingDevice.FINAL_ACTION_READ, + clear_color_values) + rd.draw_list_bind_render_pipeline(draw_list, _rasterize_render_pipeline) + + # index bufferの作成 + var index_bytes := path.indices.to_byte_array() + var index_buffer := rd.index_buffer_create(path.indices.size(), RenderingDevice.INDEX_BUFFER_FORMAT_UINT32, index_bytes) + var index_array := rd.index_array_create(index_buffer, 0, path.indices.size()) + + # vertex bufferの作成 + var vertex_bytes := path.vertices.to_byte_array() + var vertex_buffers := [rd.vertex_buffer_create(vertex_bytes.size(), vertex_bytes)] + var vertex_array := rd.vertex_array_create(path.vertices.size(), _rasterize_vertex_format, vertex_buffers) + + # framebufferのuniformのバインド + var framebuffer_texture_uniform := RDUniform.new() + framebuffer_texture_uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_SAMPLER_WITH_TEXTURE + framebuffer_texture_uniform.binding = 0 + framebuffer_texture_uniform.add_id(_sampler) + framebuffer_texture_uniform.add_id(_rasterize_texture_rids[1 - framebuffer_index]) + var framebuffer_texture_set := rd.uniform_set_create([framebuffer_texture_uniform], _rasterize_render_shader, 0) + + # push_counstantsを詰める + var rasterize_push_constant := PackedInt32Array() + rasterize_push_constant.push_back(int(Main.document_size.x)) + rasterize_push_constant.push_back(int(Main.document_size.y)) + if path.boolean == Path.Boolean.Union: + rasterize_push_constant.push_back(0) + elif path.boolean == Path.Boolean.Diff: + rasterize_push_constant.push_back(1) + elif path.boolean == Path.Boolean.Intersect: + rasterize_push_constant.push_back(2) + elif path.boolean == Path.Boolean.Xor: + rasterize_push_constant.push_back(3) + rasterize_push_constant.push_back(0) # dummy + var rasterize_push_constant_bytes := rasterize_push_constant.to_byte_array() + + rd.draw_list_bind_index_array(draw_list, index_array) + rd.draw_list_bind_vertex_array(draw_list, vertex_array) + rd.draw_list_bind_uniform_set(draw_list, framebuffer_texture_set, 0) + rd.draw_list_set_push_constant(draw_list, rasterize_push_constant_bytes, rasterize_push_constant_bytes.size()) + rd.draw_list_draw(draw_list, true, 1) + + # intersectの2つ目のパス + if path.boolean == Path.Boolean.Intersect: + rd.draw_list_end() + framebuffer_index = 1 - framebuffer_index + rd.texture_copy( + _rasterize_texture_rids[1 - framebuffer_index], + _rasterize_texture_rids[framebuffer_index], + Vector3(0, 0, 0), + Vector3(0, 0, 0), + Vector3(texture_size.x * 2, texture_size.y * 2, 0), + 0, 0, 0, 0) + draw_list = rd.draw_list_begin( + _rasterize_framebuffers[framebuffer_index], + RenderingDevice.INITIAL_ACTION_KEEP, + RenderingDevice.FINAL_ACTION_READ, + RenderingDevice.INITIAL_ACTION_KEEP, + RenderingDevice.FINAL_ACTION_READ, + clear_color_values) + rd.draw_list_bind_render_pipeline(draw_list, _rasterize_render_pipeline) + + # vertex bufferの作成 + var rect_vertices := PackedVector2Array([ + Vector2(0, 0), + Vector2(texture_size.x * 2, 0), + Vector2(texture_size.x * 2, texture_size.y * 2), + Vector2(0, 0), + Vector2(texture_size.x * 2, texture_size.y * 2), + Vector2(0, texture_size.y * 2), + ]) + var rect_vertex_bytes := rect_vertices.to_byte_array() + var rect_vertex_buffers := [rd.vertex_buffer_create(rect_vertex_bytes.size(), rect_vertex_bytes)] + var rect_vertex_array := rd.vertex_array_create(6, _rasterize_vertex_format, rect_vertex_buffers) + + # framebufferのuniformのバインド + var rect_framebuffer_texture_uniform := RDUniform.new() + rect_framebuffer_texture_uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_SAMPLER_WITH_TEXTURE + rect_framebuffer_texture_uniform.binding = 0 + rect_framebuffer_texture_uniform.add_id(_sampler) + rect_framebuffer_texture_uniform.add_id(_rasterize_texture_rids[1 - framebuffer_index]) + var rect_framebuffer_texture_set := rd.uniform_set_create([rect_framebuffer_texture_uniform], _rasterize_render_shader, 0) + + # push_counstantsを詰める + var rect_rasterize_push_constant := PackedInt32Array() + rect_rasterize_push_constant.push_back(texture_size.x * 2) + rect_rasterize_push_constant.push_back(texture_size.y * 2) + rect_rasterize_push_constant.push_back(5) + rect_rasterize_push_constant.push_back(0) # dummy + var rect_rasterize_push_constant_bytes := rect_rasterize_push_constant.to_byte_array() + + rd.draw_list_bind_vertex_array(draw_list, rect_vertex_array) + rd.draw_list_bind_uniform_set(draw_list, rect_framebuffer_texture_set, 0) + rd.draw_list_set_push_constant(draw_list, rect_rasterize_push_constant_bytes, rect_rasterize_push_constant_bytes.size()) + rd.draw_list_draw(draw_list, false, 1) + + rd.draw_list_end() + + # === Jump FLood Algorithmの初期 === + + # rasterize画像のuniform + var rasterize := RDUniform.new() + rasterize.uniform_type = RenderingDevice.UNIFORM_TYPE_IMAGE + rasterize.binding = 0 + rasterize.add_id(_rasterize_texture_rids[framebuffer_index]) + var rasterize_set := rd.uniform_set_create([rasterize], _init_jfa_line_shader, 1) + + # push_counstantsを詰める + var init_push_constant := PackedInt32Array() + init_push_constant.push_back(texture_size.x * 2) + init_push_constant.push_back(texture_size.y * 2) + init_push_constant.push_back(0) # padding + init_push_constant.push_back(0) # padding + + # コマンドを発行 + var init_compute_list := rd.compute_list_begin() + rd.compute_list_bind_compute_pipeline(init_compute_list, _init_jfa_line_pipeline) + rd.compute_list_bind_uniform_set(init_compute_list, _init_jfa_texture_set, 0) + rd.compute_list_bind_uniform_set(init_compute_list, rasterize_set, 1) + rd.compute_list_set_push_constant(init_compute_list, init_push_constant.to_byte_array(), init_push_constant.size() * 4) + rd.compute_list_dispatch(init_compute_list, x_group, y_group, 1) + rd.compute_list_end() + + # === Jump FLood Algorithm本体 === + + var space := 1 + while line_width >= space: + space *= 2 + + var tex_index := 0 + while space > 1: + # push_counstantsを詰める + var jfa_push_constant := PackedInt32Array() + jfa_push_constant.push_back(texture_size.x * 2) + jfa_push_constant.push_back(texture_size.y * 2) + jfa_push_constant.push_back(space / 2) + jfa_push_constant.push_back(0) # padding + + # コマンドを発行 + var jfa_compute_list := rd.compute_list_begin() + rd.compute_list_bind_compute_pipeline(jfa_compute_list, _jfa_line_pipeline) + rd.compute_list_bind_uniform_set(jfa_compute_list, _jfa_texture_sets[1 - tex_index], 0) + rd.compute_list_bind_uniform_set(jfa_compute_list, _jfa_texture_sets[tex_index], 1) + rd.compute_list_set_push_constant(jfa_compute_list, jfa_push_constant.to_byte_array(), jfa_push_constant.size() * 4) + rd.compute_list_dispatch(jfa_compute_list, x_group, y_group, 1) + rd.compute_list_end() + + tex_index = 1 - tex_index + space /= 2 + + # === Jump FLood Algorithmの結果をもとにcompositeする === + + # 出力画像のuniform + var internal_texture_uniform := RDUniform.new() + internal_texture_uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_IMAGE + internal_texture_uniform.binding = 0 + internal_texture_uniform.add_id(_internal_texture_rid) + var internal_texture_set := rd.uniform_set_create([internal_texture_uniform], _path_composite_shader, 0) + + # 塗りのグラデーションのテクスチャのuniform + var fill_gradient_texture_uniform := RDUniform.new() + fill_gradient_texture_uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_SAMPLER_WITH_TEXTURE + fill_gradient_texture_uniform.binding = 0 + fill_gradient_texture_uniform.add_id(_sampler) + if fill_material is ColorPaintMaterial: + fill_gradient_texture_uniform.add_id(RenderingServer.texture_get_rd_texture(RenderingServer.get_white_texture())) + elif fill_material is LinearGradientPaintMaterial: + var linear_gradient_material := fill_material as LinearGradientPaintMaterial + var gradient_rid := RenderingServer.texture_get_rd_texture(linear_gradient_material.gradient_texture) + fill_gradient_texture_uniform.add_id(gradient_rid) + elif fill_material is RadialGradientPaintMaterial: + var radial_gradient_material := fill_material as RadialGradientPaintMaterial + var gradient_rid := RenderingServer.texture_get_rd_texture(radial_gradient_material.gradient_texture) + fill_gradient_texture_uniform.add_id(gradient_rid) + elif fill_material == null: + fill_gradient_texture_uniform.add_id(RenderingServer.texture_get_rd_texture(RenderingServer.get_white_texture())) + var fill_gradient_texture_set := rd.uniform_set_create([fill_gradient_texture_uniform], _path_composite_jfa_line_shader, 2) + + # 線のグラデーションのテクスチャのuniform + var line_gradient_texture_uniform := RDUniform.new() + line_gradient_texture_uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_SAMPLER_WITH_TEXTURE + line_gradient_texture_uniform.binding = 0 + line_gradient_texture_uniform.add_id(_sampler) + if line_material is ColorPaintMaterial: + line_gradient_texture_uniform.add_id(RenderingServer.texture_get_rd_texture(RenderingServer.get_white_texture())) + elif line_material is LinearGradientPaintMaterial: + var linear_gradient_material := line_material as LinearGradientPaintMaterial + var gradient_rid := RenderingServer.texture_get_rd_texture(linear_gradient_material.gradient_texture) + line_gradient_texture_uniform.add_id(gradient_rid) + elif line_material is RadialGradientPaintMaterial: + var radial_gradient_material := line_material as RadialGradientPaintMaterial + var gradient_rid := RenderingServer.texture_get_rd_texture(radial_gradient_material.gradient_texture) + line_gradient_texture_uniform.add_id(gradient_rid) + elif line_material == null: + line_gradient_texture_uniform.add_id(RenderingServer.texture_get_rd_texture(RenderingServer.get_white_texture())) + var line_gradient_texture_set := rd.uniform_set_create([line_gradient_texture_uniform], _path_composite_jfa_line_shader, 3) + + # push_counstantsを詰める + var push_constant := PackedInt32Array() + push_constant.push_back(texture_size.x * 2) + push_constant.push_back(texture_size.y * 2) + + if fill_material is ColorPaintMaterial: + var color_material := fill_material as ColorPaintMaterial + push_constant.push_back(0) # material type + push_constant.push_back(color_material.color.r8) + push_constant.push_back(color_material.color.g8) + push_constant.push_back(color_material.color.b8) + push_constant.push_back(color_material.color.a8) + push_constant.push_back(0) # padding + var pos_bytes := PackedVector2Array([ + Vector2(0, 0), + Vector2(0, 0), + Vector2(0, 0), + ]).to_byte_array().to_int32_array() + push_constant.append_array(pos_bytes) + elif fill_material is LinearGradientPaintMaterial: + var linear_gradient_material := fill_material as LinearGradientPaintMaterial + push_constant.push_back(1) # material type + push_constant.push_back(0) + push_constant.push_back(0) + push_constant.push_back(0) + push_constant.push_back(0) + push_constant.push_back(0) # padding + var pos_bytes := PackedVector2Array([ + linear_gradient_material.start_point, + linear_gradient_material.end_point, + Vector2(0, 0), + ]).to_byte_array().to_int32_array() + push_constant.append_array(pos_bytes) + elif fill_material is RadialGradientPaintMaterial: + var radial_gradient_material := fill_material as RadialGradientPaintMaterial + push_constant.push_back(2) # material type + push_constant.push_back(0) + push_constant.push_back(0) + push_constant.push_back(0) + push_constant.push_back(0) + push_constant.push_back(0) # padding + var pos_bytes := PackedVector2Array([ + radial_gradient_material.center_point, + radial_gradient_material.handle_1_point, + radial_gradient_material.handle_2_point, + ]).to_byte_array().to_int32_array() + push_constant.append_array(pos_bytes) + elif fill_material == null: + push_constant.push_back(3) # material type + push_constant.push_back(0) + push_constant.push_back(0) + push_constant.push_back(0) + push_constant.push_back(0) + push_constant.push_back(0) # padding + var pos_bytes := PackedVector2Array([ + Vector2(0, 0), + Vector2(0, 0), + Vector2(0, 0), + ]).to_byte_array().to_int32_array() + push_constant.append_array(pos_bytes) + + if line_material is ColorPaintMaterial: + var color_material := line_material as ColorPaintMaterial + push_constant.push_back(0) # material type + push_constant.push_back(color_material.color.r8) + push_constant.push_back(color_material.color.g8) + push_constant.push_back(color_material.color.b8) + push_constant.push_back(color_material.color.a8) + push_constant.push_back(0) # padding + var pos_bytes := PackedVector2Array([ + Vector2(0, 0), + Vector2(0, 0), + Vector2(0, 0), + ]).to_byte_array().to_int32_array() + push_constant.append_array(pos_bytes) + elif line_material is LinearGradientPaintMaterial: + var linear_gradient_material := line_material as LinearGradientPaintMaterial + push_constant.push_back(1) # material type + push_constant.push_back(0) + push_constant.push_back(0) + push_constant.push_back(0) + push_constant.push_back(0) + push_constant.push_back(0) # padding + var pos_bytes := PackedVector2Array([ + linear_gradient_material.start_point, + linear_gradient_material.end_point, + Vector2(0, 0), + ]).to_byte_array().to_int32_array() + push_constant.append_array(pos_bytes) + elif line_material is RadialGradientPaintMaterial: + var radial_gradient_material := line_material as RadialGradientPaintMaterial + push_constant.push_back(2) # material type + push_constant.push_back(0) + push_constant.push_back(0) + push_constant.push_back(0) + push_constant.push_back(0) + push_constant.push_back(0) # padding + var pos_bytes := PackedVector2Array([ + radial_gradient_material.center_point, + radial_gradient_material.handle_1_point, + radial_gradient_material.handle_2_point, + ]).to_byte_array().to_int32_array() + push_constant.append_array(pos_bytes) + elif line_material == null: + push_constant.push_back(3) # material type + push_constant.push_back(0) + push_constant.push_back(0) + push_constant.push_back(0) + push_constant.push_back(0) + push_constant.push_back(0) # padding + var pos_bytes := PackedVector2Array([ + Vector2(0, 0), + Vector2(0, 0), + Vector2(0, 0), + ]).to_byte_array().to_int32_array() + push_constant.append_array(pos_bytes) + + var line_width_float := PackedFloat32Array([line_width * texture_size.x / Main.document_size.x]) + push_constant.append_array(line_width_float.to_byte_array().to_int32_array()) + + push_constant.push_back(0) # padding + + # コマンドを発行 + var compute_list := rd.compute_list_begin() + rd.compute_list_bind_compute_pipeline(compute_list, _path_composite_jfa_line_pipeline) + rd.compute_list_bind_uniform_set(compute_list, internal_texture_set, 0) + rd.compute_list_bind_uniform_set(compute_list, _path_composite_jfa_line_texture_sets[tex_index], 1) + rd.compute_list_bind_uniform_set(compute_list, fill_gradient_texture_set, 2) + rd.compute_list_bind_uniform_set(compute_list, line_gradient_texture_set, 3) + rd.compute_list_set_push_constant(compute_list, push_constant.to_byte_array(), push_constant.size() * 4) + rd.compute_list_dispatch(compute_list, x_group, y_group, 1) + rd.compute_list_end() + + # === 縮小して合成結果を書き出す === + + var x_group_scale_down := (texture_size.x - 1) / 8 + 1 + var y_group_scale_down := (texture_size.y - 1) / 8 + 1 + + # 出力画像のuniform + var texture_uniform := RDUniform.new() + texture_uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_IMAGE + texture_uniform.binding = 0 + texture_uniform.add_id(composite_texture.texture_rid) + var texture_set := rd.uniform_set_create([texture_uniform], _scale_down_shader, 0) + + # 入力画像のuniform + var scale_down_baseLtexture_uniform := RDUniform.new() + scale_down_baseLtexture_uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_SAMPLER_WITH_TEXTURE + scale_down_baseLtexture_uniform.binding = 0 + scale_down_baseLtexture_uniform.add_id(_sampler) + scale_down_baseLtexture_uniform.add_id(_internal_texture_rid) + var scale_down_baseLtexture_set := rd.uniform_set_create([scale_down_baseLtexture_uniform], _scale_down_shader, 1) + + # push_counstantsを詰める + var scale_down_push_constant := PackedInt32Array() + scale_down_push_constant.push_back(texture_size.x * 1) + scale_down_push_constant.push_back(texture_size.y * 1) + scale_down_push_constant.push_back(0) # padding + scale_down_push_constant.push_back(0) # padding + + # コマンドを発行 + var scale_down_compute_list := rd.compute_list_begin() + rd.compute_list_bind_compute_pipeline(scale_down_compute_list, _scale_down_pipeline) + rd.compute_list_bind_uniform_set(scale_down_compute_list, texture_set, 0) + rd.compute_list_bind_uniform_set(scale_down_compute_list, scale_down_baseLtexture_set, 1) + rd.compute_list_set_push_constant(scale_down_compute_list, scale_down_push_constant.to_byte_array(), scale_down_push_constant.size() * 4) + rd.compute_list_dispatch(scale_down_compute_list, x_group_scale_down, y_group_scale_down, 1) + rd.compute_list_end() + + +func path_composite_jfa_line(composite_texture: PaintCompositor.TextureHandle, path_layer: PathPaintLayer, fill_material: PaintMaterial, line_material: PaintMaterial, line_width: float) -> void: + RenderingServer.call_on_render_thread(_path_composite_jfa_line_render_process.bind(composite_texture, path_layer, fill_material, line_material, line_width)) diff --git a/godot/src/compositor/root_paint_compositor.gd b/godot/src/compositor/root_paint_compositor.gd new file mode 100644 index 0000000..d38e7b8 --- /dev/null +++ b/godot/src/compositor/root_paint_compositor.gd @@ -0,0 +1,180 @@ +## 複数のレイヤーをアルファブレンドしながら合成するcompositor。 +## GroupPaintCompositorの機能に加えてmipmapを6段階生成している。 +class_name RootPaintCompositor +extends RefCounted + + +var texture: Texture2DRD + +var texture_size: Vector2i + +## コンピュートシェーダー。 +var _shader: RID +## コンピュートパイプライン。 +var _pipeline: RID + +## mipamp生成のコンピュートシェーダー。 +var _mipmap_shader: RID +## mipamp生成のコンピュートパイプライン。 +var _mipmap_pipeline: RID + +## サンプラー。 +var _sampler: RID + + +func _init() -> void: + texture = Texture2DRD.new() + RenderingServer.call_on_render_thread(_initialize_compute_shader) + + +func _notification(what: int) -> void: + if what == NOTIFICATION_PREDELETE: + var free_compute_resource := func(shader: RID, mipmap_shader: RID, sampler: RID) -> void: + var rd := RenderingServer.get_rendering_device() + if shader: + rd.free_rid(shader) + if mipmap_shader: + rd.free_rid(mipmap_shader) + if sampler: + rd.free_rid(sampler) + RenderingServer.call_on_render_thread(free_compute_resource.bind(_shader, _mipmap_shader, _sampler)) + + +func _initialize_compute_shader() -> void: + var rd := RenderingServer.get_rendering_device() + + var shader_file: RDShaderFile = load("res://shaders/composite/group_composite.glsl") + var shader_spirv := shader_file.get_spirv() + _shader = rd.shader_create_from_spirv(shader_spirv) + _pipeline = rd.compute_pipeline_create(_shader) + + var mipmap_shader_file: RDShaderFile = load("res://shaders/composite/mipmap_generate.glsl") + var mipmap_shader_spirv := mipmap_shader_file.get_spirv() + _mipmap_shader = rd.shader_create_from_spirv(mipmap_shader_spirv) + _mipmap_pipeline = rd.compute_pipeline_create(_mipmap_shader) + + var sampler_state := RDSamplerState.new() + sampler_state.min_filter = RenderingDevice.SAMPLER_FILTER_LINEAR + sampler_state.mag_filter = RenderingDevice.SAMPLER_FILTER_LINEAR + sampler_state.mip_filter = RenderingDevice.SAMPLER_FILTER_LINEAR + _sampler = rd.sampler_create(sampler_state) + + +func resize(size: Vector2i) -> void: + if texture_size != size: + texture_size = size + + +func _render_process(output_texture: PaintCompositor.MipTextureHandle, texture_rids: Array[RID], blend_modes: Array[PaintLayer.BlendMode], clippings: Array[bool], alphas: Array[int]) -> void: + var rd := RenderingServer.get_rendering_device() + + if not rd.texture_is_valid(output_texture.texture_rid): + return + for rid in texture_rids: + if not rd.texture_is_valid(rid): + return + + var x_group := (texture_size.x * 1 - 1) / 8 + 1 + var y_group := (texture_size.y * 1 - 1) / 8 + 1 + + var texture_uniform := RDUniform.new() + texture_uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_IMAGE + texture_uniform.binding = 0 + texture_uniform.add_id(output_texture.texture_rid) + var texture_set := rd.uniform_set_create([texture_uniform], _shader, 0) + rd.texture_clear(output_texture.texture_rid, Color(0, 0, 0, 0), 0, 1, 0, 1) + + for index in texture_rids.size(): + # 合成のforeground画像をbindする + var foreground := RDUniform.new() + foreground.uniform_type = RenderingDevice.UNIFORM_TYPE_IMAGE + foreground.binding = 0 + foreground.add_id(texture_rids[index]) + var foreground_set := rd.uniform_set_create([foreground], _shader, 1) + + # 合成のclippingの親に当たる画像を探してbindする + var clipping_index := -1 + if clippings[index]: + for i in index: + if not clippings[index - i - 1]: + clipping_index = index - i - 1 + break + var clipping := RDUniform.new() + clipping.uniform_type = RenderingDevice.UNIFORM_TYPE_IMAGE + clipping.binding = 0 + if clipping_index == -1: + clipping.add_id(output_texture.texture_rid) + else: + clipping.add_id(texture_rids[clipping_index]) + var clipping_set := rd.uniform_set_create([clipping], _shader, 2) + + # クリッピングのアルファ値も計算する + var clipping_alpha := 100 + if clipping_index != -1: + clipping_alpha = alphas[clipping_index] + + # push_counstantsを詰める + var push_constant := PackedInt32Array() + push_constant.push_back(blend_modes[index]) + push_constant.push_back(clippings[index]) + push_constant.push_back(texture_size.x * 1) + push_constant.push_back(texture_size.y * 1) + push_constant.push_back(alphas[index]) + push_constant.push_back(clipping_alpha) + push_constant.push_back(1 if Main.mirror else 0) + push_constant.push_back(0) # dummy + + # コマンドを発行 + var compute_list := rd.compute_list_begin() + rd.compute_list_bind_compute_pipeline(compute_list, _pipeline) + rd.compute_list_bind_uniform_set(compute_list, texture_set, 0) + rd.compute_list_bind_uniform_set(compute_list, foreground_set, 1) + rd.compute_list_bind_uniform_set(compute_list, clipping_set, 2) + rd.compute_list_set_push_constant(compute_list, push_constant.to_byte_array(), push_constant.size() * 4) + rd.compute_list_dispatch(compute_list, x_group, y_group, 1) + rd.compute_list_end() + + # mipmapの生成 + var mipmap_in_texture_sets: Array[RID] = [] + var mipmap_out_texture_sets: Array[RID] = [] + for i in output_texture.mip_size - 1: + var in_uniform := RDUniform.new() + in_uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_SAMPLER_WITH_TEXTURE + in_uniform.binding = 0 + in_uniform.add_id(_sampler) + in_uniform.add_id(output_texture.mipmap_texture_rids[i]) + mipmap_in_texture_sets.append(rd.uniform_set_create([in_uniform], _mipmap_shader, 1)) + var out_uniform := RDUniform.new() + out_uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_IMAGE + out_uniform.binding = 0 + out_uniform.add_id(output_texture.mipmap_texture_rids[i + 1]) + mipmap_out_texture_sets.append(rd.uniform_set_create([out_uniform], _mipmap_shader, 0)) + + for i in output_texture.mip_size - 1: + var x_group_mipmap := (texture_size.x * 1 / (2 ** (i + 1)) - 1) / 8 + 1 + var y_group_mipmap := (texture_size.y * 1 / (2 ** (i + 1)) - 1) / 8 + 1 + + # push_counstantsを詰める + var push_constant := PackedInt32Array() + push_constant.push_back(texture_size.x * 1 / (2 ** (i + 1))) + push_constant.push_back(texture_size.y * 1 / (2 ** (i + 1))) + push_constant.push_back(0) # dummy + push_constant.push_back(0) # dummy + + # コマンドを発行 + var compute_list := rd.compute_list_begin() + rd.compute_list_bind_compute_pipeline(compute_list, _mipmap_pipeline) + rd.compute_list_bind_uniform_set(compute_list, mipmap_out_texture_sets[i], 0) + rd.compute_list_bind_uniform_set(compute_list, mipmap_in_texture_sets[i], 1) + rd.compute_list_set_push_constant(compute_list, push_constant.to_byte_array(), push_constant.size() * 4) + rd.compute_list_dispatch(compute_list, x_group_mipmap, y_group_mipmap, 1) + rd.compute_list_end() + + +func composite(output_texture: PaintCompositor.MipTextureHandle, texture_rids: Array[RID], blend_modes: Array[PaintLayer.BlendMode], clippings: Array[bool], alphas: Array[int]) -> void: + RenderingServer.call_on_render_thread(_render_process.bind(output_texture, texture_rids, blend_modes, clippings, alphas)) + + +## コンポジットした結果のミップマップ付きのテクスチャを取得 +func get_mipmap_texture() -> Texture2D: + return texture diff --git a/godot/src/control/control_linear_gradient.gd b/godot/src/control/control_linear_gradient.gd new file mode 100644 index 0000000..a07bd9c --- /dev/null +++ b/godot/src/control/control_linear_gradient.gd @@ -0,0 +1,178 @@ +class_name ControlLinearGradient +extends Node2D + + +## 操作が確定したときに呼ばれるシグナル。 +signal on_manipulate_finished() + +@onready var _line2d: Line2D = %Line2D +@onready var _start_point: Node2D = %StartPoint +@onready var _start_sprite: Sprite2D = %StartSprite2D +@onready var _start_sprite2: Sprite2D = %StartSprite2D2 +@onready var _end_point: Node2D = %EndPoint +@onready var _end_sprite: Sprite2D = %EndSprite2D +@onready var _end_sprite2: Sprite2D = %EndSprite2D2 + +## 開始点にマウスカーソルが乗っているかどうか。 +var _mouse_over_start: bool = false +## 終了点にマウスカーソルが乗っているかどうか。 +var _mouse_over_end: bool = false + +## 非表示かどうか。 +var _hide: bool = false + +## マウスを押している状態かどうか。 +var _mouse_pressing: bool = false +## マウスがmouse downしたあとに動いたかどうか。 +var _mouse_moving: bool = false + +## 開始点を操作中かどうか。 +var _manipulating_start: bool = false +## 終了点を操作中かどうか。 +var _manipulating_end: bool = false + +## viewportの拡大率 +var _viewport_scale: float = 1.0 + +## mouse upでヒストリを積む必要があるかどうか。 +var _dirty_history: bool = false + +## 現在編集中のマテリアル。 +var _editing_material: LinearGradientPaintMaterial + + +func _ready() -> void: + _start_sprite.material = _start_sprite.material.duplicate() + _start_sprite2.material = _start_sprite2.material.duplicate() + _end_sprite.material = _end_sprite.material.duplicate() + _end_sprite2.material = _end_sprite2.material.duplicate() + + +func _process(_delta: float) -> void: + if _hide: + _line2d.default_color = Color.TRANSPARENT + else: + _line2d.default_color = Color(1, 1, 1, 0.75) + + var start_material1 := _start_sprite.material as ShaderMaterial + var start_material2 := _start_sprite2.material as ShaderMaterial + var end_material1 := _end_sprite.material as ShaderMaterial + var end_material2 := _end_sprite2.material as ShaderMaterial + if _hide: + start_material1.set_shader_parameter("fill_color", Color.TRANSPARENT) + start_material2.set_shader_parameter("fill_color", Color.TRANSPARENT) + end_material1.set_shader_parameter("fill_color", Color.TRANSPARENT) + end_material2.set_shader_parameter("fill_color", Color.TRANSPARENT) + elif _mouse_over_start: + start_material1.set_shader_parameter("fill_color", Color.AQUA) + start_material2.set_shader_parameter("fill_color", Color.BLACK) + end_material1.set_shader_parameter("fill_color", Color.WHITE) + end_material2.set_shader_parameter("fill_color", Color.BLACK) + elif _mouse_over_end: + start_material1.set_shader_parameter("fill_color", Color.WHITE) + start_material2.set_shader_parameter("fill_color", Color.BLACK) + end_material1.set_shader_parameter("fill_color", Color.AQUA) + end_material2.set_shader_parameter("fill_color", Color.BLACK) + else: + start_material1.set_shader_parameter("fill_color", Color.WHITE) + start_material2.set_shader_parameter("fill_color", Color.BLACK) + end_material1.set_shader_parameter("fill_color", Color.WHITE) + end_material2.set_shader_parameter("fill_color", Color.BLACK) + + if _editing_material != null: + _start_point.position = _editing_material.start_point + _end_point.position = _editing_material.end_point + if Main.mirror: + _start_point.position.x = Main.document_size.x - _start_point.position.x + _end_point.position.x = Main.document_size.x - _end_point.position.x + _start_point.scale = Vector2(1.0 / _viewport_scale, 1.0 / _viewport_scale) + _end_point.scale = Vector2(1.0 / _viewport_scale, 1.0 / _viewport_scale) + _line2d.points = [_editing_material.start_point, _editing_material.end_point] + + +func _input(event: InputEvent) -> void: + if event is InputEventMouseButton: + var mouse_buttoun_event := event as InputEventMouseButton + + # mouse down + if not _mouse_pressing and mouse_buttoun_event.pressed and mouse_buttoun_event.button_index == MOUSE_BUTTON_LEFT: + if _mouse_over_start or _mouse_over_end: + if _mouse_over_end: + _manipulating_end = true + elif _mouse_over_start: + _manipulating_start = true + _mouse_pressing = true + _mouse_moving = false + + # mouse up + if _mouse_pressing and not mouse_buttoun_event.pressed and mouse_buttoun_event.button_index == MOUSE_BUTTON_LEFT: + _mouse_pressing = false + _mouse_moving = false + _manipulating_end = false + _manipulating_start = false + + if _dirty_history: + _dirty_history = false + Main.commit_history() + on_manipulate_finished.emit() + + if event is InputEventMouseMotion: + var mouse_motion_event := event as InputEventMouseMotion + + # mouse drag + if _mouse_pressing: + _mouse_moving = true + var delta := mouse_motion_event.relative / _viewport_scale + if Main.mirror: + delta.x = -delta.x + if _manipulating_end: + _editing_material.end_point += delta + elif _manipulating_start: + _editing_material.start_point += delta + Main.on_change_material_parameters_changed.emit() + _dirty_history = true + + +## グラデーションのマテリアルを設定する。 +func set_gradeint_material(gradient_material: LinearGradientPaintMaterial) -> void: + _editing_material = gradient_material + + +## ControlPointの表示上のスケールを設定する。 +func set_control_scale(new_scale: float) -> void: + _viewport_scale = new_scale + + +## このコントローラーを操作中かどうか。 +func is_manipulating() -> bool: + return _mouse_moving + + +## 非表示にする。 +func control_hide() -> void: + _hide = true + + +## 表示する。 +func control_show() -> void: + _hide = false + + +## マウスが開始点に乗ったときのコールバック。 +func _on_start_area_2d_mouse_entered() -> void: + _mouse_over_start = true + + +## マウスが開始点から離れたときのコールバック。 +func _on_start_area_2d_mouse_exited() -> void: + _mouse_over_start = false + + +## マウスが終了点に乗ったときのコールバック。 +func _on_end_area_2d_mouse_entered() -> void: + _mouse_over_end = true + + +## マウスが終了点から離れたときのコールバック。 +func _on_end_area_2d_mouse_exited() -> void: + _mouse_over_end = false diff --git a/godot/src/control/control_point.gd b/godot/src/control/control_point.gd new file mode 100644 index 0000000..7215761 --- /dev/null +++ b/godot/src/control/control_point.gd @@ -0,0 +1,229 @@ +## ユーザー制御点の挙動を記述するクラス。 +class_name ControlPoint +extends Node2D + + +## 操作が確定したときに呼ばれるシグナル。 +signal on_manipulate_finished() + +@onready var _sprite: Sprite2D = $Sprite2D +@onready var _sprite2: Sprite2D = $Sprite2D2 + +## ControlPointが属するパスの参照。 +var path: Path +## ControlPointのパスの中でのインデックス。 +var control_point_index: int = 0 + +## ControlPointのコントロールモードの列挙子。 +enum ControlMode { + NOOP, + MOVE_POSITION, + CHANGE_WEIGHT, +} +## 現在のControlPointのモード。 +var mode: ControlMode = ControlMode.NOOP + +## 現在のweightの値。 +var weight: float = 1.0 + +## マウスカーソルが乗っているかどうか。 +var mouse_over: bool = false + +## このCPが選択中かどうか。 +var selected: bool = false + +const WEIGHT_MIN: float = 0.001 +const WEIGHT_MAX: float = 100.0 + +## ホバーしているかどうか。 +## ホバーは全コントロールポイントで排他制御される。 +var _hover: bool = false + +## control_transformがホバー状態かどうか。 +var _control_transform_hover: bool = false + +## マウスを押している状態かどうか。 +var _mouse_pressing: bool = false +## マウスがmouse downしたあとに動いたかどうか。 +var _mouse_moving: bool = false + +## viewportの拡大率 +var _viewport_scale: float = 1.0 + +## mouse upでヒストリを積む必要があるかどうか。 +var _dirty_history: bool = false + +## ControlPointのscene。 +static var _control_point_scene: PackedScene + +## 非表示かどうか。 +var _hide: bool = false + + +func _ready() -> void: + _sprite.material = _sprite.material.duplicate() + _sprite2.material = _sprite2.material.duplicate() + + +func _process(_delta: float) -> void: + var material1 := _sprite.material as ShaderMaterial + var material2 := _sprite2.material as ShaderMaterial + if _hide: + material1.set_shader_parameter("fill_color", Color.TRANSPARENT) + material2.set_shader_parameter("fill_color", Color.TRANSPARENT) + elif selected: + material1.set_shader_parameter("fill_color", Color.AQUA) + material2.set_shader_parameter("fill_color", Color.BLACK) + elif _hover: + material1.set_shader_parameter("fill_color", Color(1, 0.7, 1, 1)) + material2.set_shader_parameter("fill_color", Color.BLACK) + else: + material1.set_shader_parameter("fill_color", Color.WHITE) + material2.set_shader_parameter("fill_color", Color.BLACK) + + +func _input(event: InputEvent) -> void: + if event is InputEventMouseButton: + var mouse_buttoun_event := event as InputEventMouseButton + + # mouse down + if not _mouse_pressing and mouse_buttoun_event.pressed and mouse_buttoun_event.button_index == MOUSE_BUTTON_LEFT: + if _hover and not _control_transform_hover: + _mouse_pressing = true + _mouse_moving = false + + # mouse up + if _mouse_pressing and not mouse_buttoun_event.pressed and mouse_buttoun_event.button_index == MOUSE_BUTTON_LEFT: + _mouse_pressing = false + _mouse_moving = false + + if _dirty_history: + _dirty_history = false + Main.commit_history() + emit_manipulate_finish_next_frame() + + if event is InputEventMouseMotion: + var mouse_motion_event := event as InputEventMouseMotion + + # mouse drag + if _mouse_pressing: + _mouse_moving = true + + if mode == ControlMode.NOOP: + pass + elif mode == ControlMode.MOVE_POSITION: + selected = true + var node := $"." as Node2D + var delta := mouse_motion_event.relative / _viewport_scale + node.position += delta + var pos := node.position + if Main.mirror: + pos.x = Main.document_size.x - pos.x + path.move_control_point(pos, control_point_index) + _dirty_history = true + elif mode == ControlMode.CHANGE_WEIGHT: + var log_weight := log(weight) + log_weight += mouse_motion_event.relative.x / 100.0 + weight = exp(log_weight) + weight = clampf(weight, WEIGHT_MIN, WEIGHT_MAX) + path.change_weight(weight, control_point_index) + _dirty_history = true + + +## 操作完了の通知を次のフレームに出す。 +func emit_manipulate_finish_next_frame() -> void: + await (Engine.get_main_loop() as SceneTree).process_frame + on_manipulate_finished.emit() + + +## ControlPointの位置を設定する。 +func set_control_position(pos: Vector2) -> void: + var node := $"." as Node2D + node.position = pos + if Main.mirror: + pos.x = Main.document_size.x - pos.x + path.move_control_point(pos, control_point_index) + + +## ControlPointの位置を取得する。 +func get_control_position() -> Vector2: + var node := $"." as Node2D + return node.position + + +## ControlPointの表示上のスケールを設定する。 +func set_control_scale(new_scale: float) -> void: + _viewport_scale = new_scale + var node := $"." as Node2D + node.scale = Vector2(1.0 / _viewport_scale, 1.0 / _viewport_scale) + + +## ControlPointのコントロールモードを設定する。 +func set_mode(new_mode: ControlMode) -> void: + mode = new_mode + + +## ホバーしているかどうかをセットする。 +## ホバーは全コントロールポイントで排他制御される。 +func set_hover(hover: bool) -> void: + _hover = hover + + +## ホバー状態を取得する。 +func get_hover() -> bool: + return _hover + + +## このCPを削除する。 +func delete_cp() -> void: + path.delete_control_point(control_point_index) + + +## control_transformがホバー状態かをセットする。 +func set_control_transform_hover(hover: bool) -> void: + _control_transform_hover = hover + + +## 現在このコントローラーの値を変更中かどうか。 +func is_changing_parameter() -> bool: + return _mouse_pressing + + +## このコントローラーを操作中かどうか。 +func is_manipulating() -> bool: + return _mouse_moving + + +## 非表示にする。 +func control_hide() -> void: + _hide = true + + +## 表示する。 +func control_show() -> void: + _hide = false + + +## マウスオーバーしたときに呼ばれるコールバック。 +func _on_area_2d_mouse_entered() -> void: + mouse_over = true + + +## マウスが外れたときに呼ばれるコールバック。 +func _on_area_2d_mouse_exited() -> void: + mouse_over = false + + +## ControlPointを作成する関数。 +static func new_control_point(new_path: Path, index: int) -> ControlPoint: + if not _control_point_scene: + _control_point_scene = load("res://scenes/control/control_point.tscn") + var cp_data := new_path.control_points[index] + if Main.mirror: + cp_data.x = Main.document_size.x - cp_data.x + var cp := _control_point_scene.instantiate() as ControlPoint + cp.path = new_path + cp.control_point_index = index + cp.set_control_position(cp_data) + cp.weight = new_path.weights[index] + return cp diff --git a/godot/src/control/control_radial_gradient.gd b/godot/src/control/control_radial_gradient.gd new file mode 100644 index 0000000..ef3c8c4 --- /dev/null +++ b/godot/src/control/control_radial_gradient.gd @@ -0,0 +1,234 @@ +class_name ControlRadialGradient +extends Node2D + + +## 操作が確定したときに呼ばれるシグナル。 +signal on_manipulate_finished() + +@onready var _line2d1: Line2D = %Line2D1 +@onready var _line2d2: Line2D = %Line2D2 +@onready var _center_point: Node2D = %CenterPoint +@onready var _center_sprite: Sprite2D = %CenterSprite2D +@onready var _center_sprite2: Sprite2D = %CenterSprite2D2 +@onready var _handle_1_point: Node2D = %Handle1Point +@onready var _handle_1_sprite: Sprite2D = %Handle1Sprite2D +@onready var _handle_1_sprite2: Sprite2D = %Handle1Sprite2D2 +@onready var _handle_2_point: Node2D = %Handle2Point +@onready var _handle_2_sprite: Sprite2D = %Handle2Sprite2D +@onready var _handle_2_sprite2: Sprite2D = %Handle2Sprite2D2 + +## 中心点にマウスカーソルが乗っているかどうか。 +var _mouse_over_center: bool = false +## ハンドル1にマウスカーソルが乗っているかどうか。 +var _mouse_over_handle_1: bool = false +## ハンドル2にマウスカーソルが乗っているかどうか。 +var _mouse_over_handle_2: bool = false + +## 非表示かどうか。 +var _hide: bool = false + +## マウスを押している状態かどうか。 +var _mouse_pressing: bool = false +## マウスがmouse downしたあとに動いたかどうか。 +var _mouse_moving: bool = false + +## 中心点を操作中かどうか。 +var _manipulating_center: bool = false +## ハンドル1を操作中かどうか。 +var _manipulating_handle_1: bool = false +## ハンドル2を操作中かどうか。 +var _manipulating_handle_2: bool = false + +## viewportの拡大率 +var _viewport_scale: float = 1.0 + +## mouse upでヒストリを積む必要があるかどうか。 +var _dirty_history: bool = false + +## 現在編集中のマテリアル。 +var _editing_material: RadialGradientPaintMaterial + + +func _ready() -> void: + _center_sprite.material = _center_sprite.material.duplicate() + _center_sprite2.material = _center_sprite2.material.duplicate() + _handle_1_sprite.material = _handle_1_sprite.material.duplicate() + _handle_1_sprite2.material = _handle_1_sprite2.material.duplicate() + _handle_2_sprite.material = _handle_2_sprite.material.duplicate() + _handle_2_sprite2.material = _handle_2_sprite2.material.duplicate() + + +func _process(_delta: float) -> void: + if _hide: + _line2d1.default_color = Color.TRANSPARENT + _line2d2.default_color = Color.TRANSPARENT + else: + _line2d1.default_color = Color(1, 1, 1, 0.75) + _line2d2.default_color = Color(1, 1, 1, 0.75) + + var center_material1 := _center_sprite.material as ShaderMaterial + var center_material2 := _center_sprite2.material as ShaderMaterial + var handle_1_material1 := _handle_1_sprite.material as ShaderMaterial + var handle_1_material2 := _handle_1_sprite2.material as ShaderMaterial + var handle_2_material1 := _handle_2_sprite.material as ShaderMaterial + var handle_2_material2 := _handle_2_sprite2.material as ShaderMaterial + if _hide: + center_material1.set_shader_parameter("fill_color", Color.TRANSPARENT) + center_material2.set_shader_parameter("fill_color", Color.TRANSPARENT) + handle_1_material1.set_shader_parameter("fill_color", Color.TRANSPARENT) + handle_1_material2.set_shader_parameter("fill_color", Color.TRANSPARENT) + handle_2_material1.set_shader_parameter("fill_color", Color.TRANSPARENT) + handle_2_material2.set_shader_parameter("fill_color", Color.TRANSPARENT) + elif _mouse_over_center: + center_material1.set_shader_parameter("fill_color", Color.AQUA) + center_material2.set_shader_parameter("fill_color", Color.BLACK) + handle_1_material1.set_shader_parameter("fill_color", Color.WHITE) + handle_1_material2.set_shader_parameter("fill_color", Color.BLACK) + handle_2_material1.set_shader_parameter("fill_color", Color.WHITE) + handle_2_material2.set_shader_parameter("fill_color", Color.BLACK) + elif _mouse_over_handle_1: + center_material1.set_shader_parameter("fill_color", Color.WHITE) + center_material2.set_shader_parameter("fill_color", Color.BLACK) + handle_1_material1.set_shader_parameter("fill_color", Color.AQUA) + handle_1_material2.set_shader_parameter("fill_color", Color.BLACK) + handle_2_material1.set_shader_parameter("fill_color", Color.WHITE) + handle_2_material2.set_shader_parameter("fill_color", Color.BLACK) + elif _mouse_over_handle_2: + center_material1.set_shader_parameter("fill_color", Color.WHITE) + center_material2.set_shader_parameter("fill_color", Color.BLACK) + handle_1_material1.set_shader_parameter("fill_color", Color.WHITE) + handle_1_material2.set_shader_parameter("fill_color", Color.BLACK) + handle_2_material1.set_shader_parameter("fill_color", Color.AQUA) + handle_2_material2.set_shader_parameter("fill_color", Color.BLACK) + else: + center_material1.set_shader_parameter("fill_color", Color.WHITE) + center_material2.set_shader_parameter("fill_color", Color.BLACK) + handle_1_material1.set_shader_parameter("fill_color", Color.WHITE) + handle_1_material2.set_shader_parameter("fill_color", Color.BLACK) + handle_2_material1.set_shader_parameter("fill_color", Color.WHITE) + handle_2_material2.set_shader_parameter("fill_color", Color.BLACK) + + if _editing_material != null: + _center_point.position = _editing_material.center_point + _handle_1_point.position = _editing_material.handle_1_point + _handle_2_point.position = _editing_material.handle_2_point + if Main.mirror: + _center_point.position.x = Main.document_size.x - _center_point.position.x + _handle_1_point.position.x = Main.document_size.x - _handle_1_point.position.x + _handle_2_point.position.x = Main.document_size.x - _handle_2_point.position.x + _center_point.scale = Vector2(1.0 / _viewport_scale, 1.0 / _viewport_scale) + _handle_1_point.scale = Vector2(1.0 / _viewport_scale, 1.0 / _viewport_scale) + _handle_2_point.scale = Vector2(1.0 / _viewport_scale, 1.0 / _viewport_scale) + _line2d1.points = [_editing_material.center_point, _editing_material.handle_1_point] + _line2d2.points = [_editing_material.center_point, _editing_material.handle_2_point] + + +func _input(event: InputEvent) -> void: + if event is InputEventMouseButton: + var mouse_buttoun_event := event as InputEventMouseButton + + # mouse down + if not _mouse_pressing and mouse_buttoun_event.pressed and mouse_buttoun_event.button_index == MOUSE_BUTTON_LEFT: + if _mouse_over_center or _mouse_over_handle_1 or _mouse_over_handle_2: + if _mouse_over_handle_1: + _manipulating_handle_1 = true + elif _mouse_over_handle_2: + _manipulating_handle_2 = true + elif _mouse_over_center: + _manipulating_center = true + _mouse_pressing = true + _mouse_moving = false + + # mouse up + if _mouse_pressing and not mouse_buttoun_event.pressed and mouse_buttoun_event.button_index == MOUSE_BUTTON_LEFT: + _mouse_pressing = false + _mouse_moving = false + _manipulating_handle_1 = false + _manipulating_handle_2 = false + _manipulating_center = false + + if _dirty_history: + _dirty_history = false + Main.commit_history() + on_manipulate_finished.emit() + + if event is InputEventMouseMotion: + var mouse_motion_event := event as InputEventMouseMotion + + # mouse drag + if _mouse_pressing: + _mouse_moving = true + var delta := mouse_motion_event.relative / _viewport_scale + if Main.mirror: + delta.x = -delta.x + if _manipulating_handle_1: + _editing_material.handle_1_point += delta + var handle_2_length := _editing_material.handle_2_point.distance_to(_editing_material.center_point) + var handle_2_dir := (_editing_material.handle_1_point - _editing_material.center_point).normalized().rotated(-PI / 2) + _editing_material.handle_2_point = _editing_material.center_point + handle_2_dir * handle_2_length + elif _manipulating_handle_2: + _editing_material.handle_2_point += delta + var handle_1_length := _editing_material.handle_1_point.distance_to(_editing_material.center_point) + var handle_1_dir := (_editing_material.handle_2_point - _editing_material.center_point).normalized().rotated(PI / 2) + _editing_material.handle_1_point = _editing_material.center_point + handle_1_dir * handle_1_length + elif _manipulating_center: + _editing_material.center_point += delta + _editing_material.handle_1_point += delta + _editing_material.handle_2_point += delta + Main.on_change_material_parameters_changed.emit() + _dirty_history = true + + +## グラデーションのマテリアルを設定する。 +func set_gradeint_material(gradient_material: RadialGradientPaintMaterial) -> void: + _editing_material = gradient_material + + +## ControlPointの表示上のスケールを設定する。 +func set_control_scale(new_scale: float) -> void: + _viewport_scale = new_scale + + +## このコントローラーを操作中かどうか。 +func is_manipulating() -> bool: + return _mouse_moving + + +## 非表示にする。 +func control_hide() -> void: + _hide = true + + +## 表示する。 +func control_show() -> void: + _hide = false + + +## マウスが中心点に乗ったときのコールバック。 +func _on_center_area_2d_mouse_entered() -> void: + _mouse_over_center = true + + +## マウスが中心点から離れたときのコールバック。 +func _on_center_area_2d_mouse_exited() -> void: + _mouse_over_center = false + + +## マウスがハンドル1に乗ったときのコールバック。 +func _on_handle_1_area_2d_mouse_entered() -> void: + _mouse_over_handle_1 = true + + +## マウスがハンドル1から離れたときのコールバック。 +func _on_handle_1_area_2d_mouse_exited() -> void: + _mouse_over_handle_1 = false + + +## マウスがハンドル2に乗ったときのコールバック。 +func _on_handle_2_area_2d_mouse_entered() -> void: + _mouse_over_handle_2 = true + + +## マウスがハンドル2から離れたときのコールバック。 +func _on_handle_2_area_2d_mouse_exited() -> void: + _mouse_over_handle_2 = false diff --git a/godot/src/control/control_segment.gd b/godot/src/control/control_segment.gd new file mode 100644 index 0000000..39f6cae --- /dev/null +++ b/godot/src/control/control_segment.gd @@ -0,0 +1,241 @@ +## 制御用セグメントの挙動を記述するクラス。 +class_name ControlSegment +extends Node2D + + +## 操作が確定したときに呼ばれるシグナル。 +signal on_manipulate_finished() + +@onready var _line2d: Line2D = $Line2D + +## ControlSegmentが属するパスの参照。 +var path: Path +## ControlSegmentのパスの中でのインデックス。 +var segment_index: int = 0 + +## ControlSegmentのコントロールモードの列挙子。 +enum ControlMode { + NOOP, + CHANGE_PARAMETER, +} +## 現在のControlSegmentのモード。 +var mode: ControlMode = ControlMode.NOOP + +## 現在のphiのパラメータの値。 +var phi: float = 1.0 +## 現在のpsiのパラメータの値。 +var psi: float = 0.0 + +const PHI_MIN: float = 0.01 +const PHI_MAX: float = 6.0 + +const PSI_MIN: float = -2.0 +const PSI_MAX: float = 2.0 + +## マウスカーソルが乗っているかどうか。 +var mouse_over: bool = false + +## 変更するパラメータの種類。 +enum ChangeMode { + NO_CHANGE, + CHANGE_PHI, + CHANGE_PSI, +} +## 現在の変更するパラメータの種類。 +var _change_mode: ChangeMode = ChangeMode.NO_CHANGE +## 変更するパラメータを決定するしきい値。 +const CHANGE_MODE_THREASHOLD: float = 5.0 + +## ホバーしているかどうか。 +## ホバーは全コントロールポイントで排他制御される。 +var _hover: bool = false + +## マウスを押している状態かどうか。 +var _mouse_pressing: bool = false +## マウスがmouse downしたあとに動いたかどうか。 +var _mouse_moving: bool = false +## マウスダウンした座標。 +var _mouse_down_point: Vector2 + +const COLLISION_WIDTH: float = 16.0 +const DISPLAY_WIDTH: float = 5.0 + +## mouse upでヒストリを積む必要があるかどうか。 +var _dirty_history: bool = false + +## _set_segmentするためのsegment。 +var _segment: PackedVector2Array + +## documentの表示スケール。 +var _viewport_scale: float = 1.0 + +## 非表示かどうか。 +var _hide: bool = false + +## ControlSegmentのscene。 +static var _control_segment_scene: PackedScene + + +func _ready() -> void: + _line2d.material = _line2d.material.duplicate() + + +func _process(_delta: float) -> void: + _update_material() + _check_mouseover() + + +func _input(event: InputEvent) -> void: + if event is InputEventMouseButton: + var mouse_buttoun_event := event as InputEventMouseButton + + # mouse down + if not _mouse_pressing and mouse_buttoun_event.pressed and mouse_buttoun_event.button_index == MOUSE_BUTTON_LEFT: + if _hover: + _mouse_pressing = true + _mouse_moving = false + _mouse_down_point = mouse_buttoun_event.position + + # mouse up + if _mouse_pressing and not mouse_buttoun_event.pressed and mouse_buttoun_event.button_index == MOUSE_BUTTON_LEFT: + _mouse_pressing = false + _mouse_moving = false + _change_mode = ChangeMode.NO_CHANGE + + if _dirty_history: + _dirty_history = false + Main.commit_history() + emit_manipulate_finish_next_frame() + + if event is InputEventMouseMotion: + var mouse_motion_event := event as InputEventMouseMotion + + # mouse drag + if _mouse_pressing: + _mouse_moving = true + + if mode == ControlMode.NOOP: + pass + elif mode == ControlMode.CHANGE_PARAMETER: + if _change_mode == ChangeMode.NO_CHANGE: + var delta := mouse_motion_event.position - _mouse_down_point + if absf(delta.x) < absf(delta.y): + if absf(delta.y) > CHANGE_MODE_THREASHOLD: + _change_mode = ChangeMode.CHANGE_PHI + else: + if absf(delta.x) > CHANGE_MODE_THREASHOLD: + _change_mode = ChangeMode.CHANGE_PSI + _dirty_history = true + elif _change_mode == ChangeMode.CHANGE_PHI: + var log_phi := log(phi) + log_phi -= mouse_motion_event.relative.y / 100.0 + phi = exp(log_phi) + phi = clampf(phi, PHI_MIN, PHI_MAX) + path.change_phi(phi, segment_index) + _dirty_history = true + elif _change_mode == ChangeMode.CHANGE_PSI: + psi += mouse_motion_event.relative.x / 100.0 + psi = clampf(psi, PSI_MIN, PSI_MAX) + path.change_psi(psi, segment_index) + _dirty_history = true + + +## 操作完了の通知を次のフレームに出す。 +func emit_manipulate_finish_next_frame() -> void: + await (Engine.get_main_loop() as SceneTree).process_frame + on_manipulate_finished.emit() + + +## セグメントの形状を設定する。 +func set_segment(segment: PackedVector2Array) -> void: + if Main.mirror: + segment = segment.duplicate() + for i in range(segment.size()): + segment[i] = Vector2(Main.document_size.x - segment[i].x, segment[i].y) + + _segment = segment + + # 描画形状を設定する + _line2d.points = _segment + _line2d.width = COLLISION_WIDTH / 2 / _viewport_scale + + +## ControlSegmentのコントロールモードを設定する。 +func set_mode(new_mode: ControlMode) -> void: + mode = new_mode + + +## ホバーしているかどうかをセットする。 +## ホバーは全コントロールポイントで排他制御される。 +func set_hover(hover: bool) -> void: + _hover = hover + + +## ホバー状態を取得する。 +func get_hover() -> bool: + return _hover + + +## ControlPointの表示上のスケールを設定する。 +func set_control_scale(new_scale: float) -> void: + _viewport_scale = new_scale + _line2d.width = COLLISION_WIDTH / 2 / _viewport_scale + + +## control pointを挿入する。 +func insert_cp(new_position: Vector2) -> void: + path.insert_control_point(new_position, segment_index + 1) + + +## 現在このコントローラーの値を変更中かどうか。 +func is_changing_parameter() -> bool: + return _mouse_pressing + + +## このコントローラーを操作中かどうか。 +func is_manipulating() -> bool: + return _mouse_moving + + +## 非表示にする。 +func control_hide() -> void: + _hide = true + + +## 表示する。 +func control_show() -> void: + _hide = false + + +## 見た目を更新する。 +func _update_material() -> void: + if _line2d.material is ShaderMaterial: + var mat := _line2d.material as ShaderMaterial + if _hide: + mat.set_shader_parameter("fill_color", Color.TRANSPARENT) + elif _hover: + mat.set_shader_parameter("fill_color", Color(0.0, 1.0, 1.0, 0.7)) + else: + mat.set_shader_parameter("fill_color", Color.TRANSPARENT) + + +## マウスオーバーを確認する。 +func _check_mouseover() -> void: + if Main.is_manipulating or _hide: + mouse_over = false + return + var mouse_position: Vector2 = (get_parent() as Control).get_local_mouse_position() + var distance := ShapeUtil.distance_segment_and_point(_segment, mouse_position) + mouse_over = distance < COLLISION_WIDTH / 2 / _viewport_scale + + +## ControlSegmentを作成する関数。 +static func new_control_segment(new_path: Path, index: int) -> ControlSegment: + if not _control_segment_scene: + _control_segment_scene = load("res://scenes/control/control_segment.tscn") + var cs := _control_segment_scene.instantiate() as ControlSegment + cs.path = new_path + cs.segment_index = index + cs.phi = new_path.phis[index] + cs.psi = new_path.psis[index] + return cs diff --git a/godot/src/control/control_transform.gd b/godot/src/control/control_transform.gd new file mode 100644 index 0000000..2707ea8 --- /dev/null +++ b/godot/src/control/control_transform.gd @@ -0,0 +1,730 @@ +class_name ControlTransform +extends Panel + + +## 操作が完了したタイミングで発火するシグナル。 +signal on_manipulate_finished() + +## コントローラーのホバー状態。 +var _hover: bool = false +## 左上の角のホバー状態。 +var _top_left_hover: bool = false +## 上の辺のホバー状態。 +var _top_hover: bool = false +## 右上の角のホバー状態。 +var _top_right_hover: bool = false +## 右の辺のホバー状態。 +var _right_hover: bool = false +## 右下の角のホバー状態。 +var _bottom_right_hover: bool = false +## 下の辺のホバー状態。 +var _bottom_hover: bool = false +## 左下の角のホバー状態。 +var _bottom_left_hover: bool = false +## 左の変のホバー状態。 +var _left_hover: bool = false +## 回転のコントローラーのホバー状態。 +var _rotation_hover: bool = false + +## マウスを押している状態かどうか。 +var _mouse_pressing: bool = false +## マウスがmouse downしたあとに動いたかどうか。 +var _mouse_moving: bool = false + +## 通常ドラッグ開始しているかどうかのフラグ。 +var _is_start_drag: bool = false +## 通常ドラッグの開始点の座標。 +var _drag_start_position: Vector2 + +## スケール開始時の位置。 +var _scale_start_position: Vector2 +## スケール開始時の大きさ。 +var _scale_start_size: Vector2 +## スケール開始時の中心位置。 +var _scale_start_center: Vector2 +## 右下角でスケール開始しているかのフラグ。 +var _is_start_scale_bottom_right: bool = false +## 下辺でスケール開始しているかのフラグ。 +var _is_start_scale_bottom: bool = false +## 右辺でスケール開始しているかのフラグ。 +var _is_start_scale_right: bool = false +## 左下角でスケール開始しているかのフラグ。 +var _is_start_scale_bottom_left: bool = false +## 右上角でスケール開始しているかのフラグ。 +var _is_start_scale_top_right: bool = false +## 左辺でスケール開始しているかのフラグ。 +var _is_start_scale_left: bool = false +## 上辺でスケール開始しているかのフラグ。 +var _is_start_scale_top: bool = false +## 左上角でスケール開始しているかのフラグ。 +var _is_start_scale_top_left: bool = false + +## 回転が開始しているかどうかのフラグ。 +var _is_start_rotation: bool = false +## 回転の中心座標。 +var _rotation_center: Vector2 + +## 非表示状態かどうかのフラグ。 +var _hide: bool = false + +## コントローラーのコントロールするControlPointの配列。 +var _control_points: Array[ControlPoint] = [] + +## ドラッグ開始時のControlPointのいち情報の配列。 +var _position_cache: Array[Vector2] = [] + + +func _process(_delta: float) -> void: + if _mouse_moving and _is_start_drag: + _drag() + elif _mouse_moving and _is_start_scale_bottom_right: + _scale_bottom_right() + elif _mouse_moving and _is_start_scale_bottom: + _scale_bottom() + elif _mouse_moving and _is_start_scale_right: + _scale_right() + elif _mouse_moving and _is_start_scale_bottom_left: + _scale_bottom_left() + elif _mouse_moving and _is_start_scale_top_right: + _scale_top_right() + elif _mouse_moving and _is_start_scale_left: + _scale_left() + elif _mouse_moving and _is_start_scale_top: + _scale_top() + elif _mouse_moving and _is_start_scale_top_left: + _scale_top_left() + elif _mouse_moving and _is_start_rotation: + _rotate() + + if _hide: + modulate = Color(1, 1, 1, 0) + else: + modulate = Color(1, 1, 1, 1) + + +func _input(event: InputEvent) -> void: + var mouse_position: Vector2 = (get_parent() as Control).get_local_mouse_position() + if event is InputEventMouseButton: + var mouse_buttoun_event := event as InputEventMouseButton + + # mouse down + if not _mouse_pressing and mouse_buttoun_event.pressed and mouse_buttoun_event.button_index == MOUSE_BUTTON_LEFT: + if is_hover(): + _mouse_pressing = true + _mouse_moving = false + _capture_control_points_position() + + if _rotation_hover: + # 回転 + _is_start_rotation = true + _rotation_center = position + pivot_offset + _drag_start_position = mouse_position + elif _top_left_hover or \ + _top_hover or \ + _left_hover or \ + _top_right_hover or \ + _bottom_left_hover or \ + _right_hover or \ + _bottom_hover or \ + _bottom_right_hover: + # スケーリング + _scale_start_position = position + _scale_start_size = size + _scale_start_center = position + size / 2 + if _bottom_right_hover: + _is_start_scale_bottom_right = true + elif _bottom_hover: + _is_start_scale_bottom = true + elif _right_hover: + _is_start_scale_right = true + elif _bottom_left_hover: + _is_start_scale_bottom_left = true + elif _top_right_hover: + _is_start_scale_top_right = true + elif _left_hover: + _is_start_scale_left = true + elif _top_hover: + _is_start_scale_top = true + elif _top_left_hover: + _is_start_scale_top_left = true + elif _hover: + # 平行移動 + _is_start_drag = true + _drag_start_position = mouse_position + + # mouse up + if _mouse_pressing and not mouse_buttoun_event.pressed and mouse_buttoun_event.button_index == MOUSE_BUTTON_LEFT: + var manipulated := false + if _mouse_pressing and _mouse_moving: + manipulated = true + + _mouse_pressing = false + _mouse_moving = false + + _is_start_rotation = false + _is_start_scale_bottom_right = false + _is_start_scale_bottom = false + _is_start_scale_right = false + _is_start_scale_bottom_left = false + _is_start_scale_top_right = false + _is_start_scale_left = false + _is_start_scale_top = false + _is_start_scale_top_left = false + _is_start_drag = false + + _reset_control() + + if manipulated: + Main.commit_history() + on_manipulate_finished.emit() + + if event is InputEventMouseMotion: + # mouse drag + if _mouse_pressing: + _mouse_moving = true + + +## ドラッグしたときの挙動。 +func _drag() -> void: + var mouse_position: Vector2 = (get_parent() as Control).get_local_mouse_position() + var delta := mouse_position - _drag_start_position + for index in _control_points.size(): + var start_pos := _position_cache[index] + var cp := _control_points[index] + cp.set_control_position(start_pos + delta) + _reset_control() + + +## 右下でスケールしたときの挙動。 +func _scale_bottom_right() -> void: + var mouse_position: Vector2 = (get_parent() as Control).get_local_mouse_position() + var ctrl_pressed := Input.is_key_pressed(KEY_CTRL) + var shift_pressed := Input.is_key_pressed(KEY_SHIFT) + if ctrl_pressed: + var new_size := (mouse_position - _scale_start_center) * 2 + var scale_x := 0.0 + if _scale_start_size.x != 0.0: + scale_x = new_size.x / _scale_start_size.x + var scale_y := 0.0 + if _scale_start_size.y != 0.0: + scale_y = new_size.y / _scale_start_size.y + var scale_shift := signf(scale_x) * minf(absf(scale_x), absf(scale_y)) + for index in _control_points.size(): + var start_pos := _position_cache[index] + var cp := _control_points[index] + if shift_pressed: + var new_pos := (start_pos - _scale_start_center) * scale_shift + _scale_start_center + cp.set_control_position(new_pos) + else: + var new_pos := (start_pos - _scale_start_center) * Vector2(scale_x, scale_y) + _scale_start_center + cp.set_control_position(new_pos) + else: + var new_size := mouse_position - _scale_start_position + var scale_anchor := _scale_start_position + var scale_x := 0.0 + if _scale_start_size.x != 0.0: + scale_x = new_size.x / _scale_start_size.x + var scale_y := 0.0 + if _scale_start_size.y != 0.0: + scale_y = new_size.y / _scale_start_size.y + var scale_shift := signf(scale_x) * minf(absf(scale_x), absf(scale_y)) + for index in _control_points.size(): + var start_pos := _position_cache[index] + var cp := _control_points[index] + if shift_pressed: + var new_pos := (start_pos - scale_anchor) * scale_shift + scale_anchor + cp.set_control_position(new_pos) + else: + var new_pos := (start_pos - scale_anchor) * Vector2(scale_x, scale_y) + scale_anchor + cp.set_control_position(new_pos) + _reset_control() + + +## 下でスケールしたときの挙動。 +func _scale_bottom() -> void: + var mouse_position: Vector2 = (get_parent() as Control).get_local_mouse_position() + var ctrl_pressed := Input.is_key_pressed(KEY_CTRL) + var shift_pressed := Input.is_key_pressed(KEY_SHIFT) + if ctrl_pressed: + var new_size_y := (mouse_position.y - _scale_start_center.y) * 2 + var scale_y := 0.0 + if _scale_start_size.y != 0.0: + scale_y = new_size_y / _scale_start_size.y + for index in _control_points.size(): + var start_pos := _position_cache[index] + var cp := _control_points[index] + if shift_pressed: + var new_pos := (start_pos - _scale_start_center) * Vector2(scale_y * signf(scale_y), scale_y) + _scale_start_center + cp.set_control_position(new_pos) + else: + var new_pos := (start_pos - _scale_start_center) * Vector2(1, scale_y) + _scale_start_center + cp.set_control_position(new_pos) + else: + var new_size_y := mouse_position.y - _scale_start_position.y + var scale_y := 0.0 + if _scale_start_size.y != 0.0: + scale_y = new_size_y / _scale_start_size.y + var scale_anchor := Vector2(_scale_start_center.x, _scale_start_position.y) + for index in _control_points.size(): + var start_pos := _position_cache[index] + var cp := _control_points[index] + if shift_pressed: + var new_pos := (start_pos - scale_anchor) * Vector2(scale_y * signf(scale_y), scale_y) + scale_anchor + cp.set_control_position(new_pos) + else: + var new_pos := (start_pos - scale_anchor) * Vector2(1, scale_y) + scale_anchor + cp.set_control_position(new_pos) + _reset_control() + + +## 右でスケールしたときの挙動。 +func _scale_right() -> void: + var mouse_position: Vector2 = (get_parent() as Control).get_local_mouse_position() + var ctrl_pressed := Input.is_key_pressed(KEY_CTRL) + var shift_pressed := Input.is_key_pressed(KEY_SHIFT) + if ctrl_pressed: + var new_size_x := (mouse_position.x - _scale_start_center.x) * 2 + var scale_x := 0.0 + if _scale_start_size.x != 0.0: + scale_x = new_size_x / _scale_start_size.x + for index in _control_points.size(): + var start_pos := _position_cache[index] + var cp := _control_points[index] + if shift_pressed: + var new_pos := (start_pos - _scale_start_center) * Vector2(scale_x, scale_x * signf(scale_x)) + _scale_start_center + cp.set_control_position(new_pos) + else: + var new_pos := (start_pos - _scale_start_center) * Vector2(scale_x, 1) + _scale_start_center + cp.set_control_position(new_pos) + else: + var new_size_x := mouse_position.x - _scale_start_position.x + var scale_x := 0.0 + if _scale_start_size.x != 0.0: + scale_x = new_size_x / _scale_start_size.x + var scale_anchor := Vector2(_scale_start_position.x, _scale_start_center.y) + for index in _control_points.size(): + var start_pos := _position_cache[index] + var cp := _control_points[index] + if shift_pressed: + var new_pos := (start_pos - scale_anchor) * Vector2(scale_x, scale_x * signf(scale_x)) + scale_anchor + cp.set_control_position(new_pos) + else: + var new_pos := (start_pos - scale_anchor) * Vector2(scale_x, 1) + scale_anchor + cp.set_control_position(new_pos) + _reset_control() + + +## 左下でスケールしたときの挙動。 +func _scale_bottom_left() -> void: + var mouse_position: Vector2 = (get_parent() as Control).get_local_mouse_position() + var ctrl_pressed := Input.is_key_pressed(KEY_CTRL) + var shift_pressed := Input.is_key_pressed(KEY_SHIFT) + if ctrl_pressed: + var new_size := Vector2((_scale_start_center.x - mouse_position.x) * 2, (mouse_position.y - _scale_start_center.y) * 2) + var scale_x := 0.0 + if _scale_start_size.x != 0.0: + scale_x = new_size.x / _scale_start_size.x + var scale_y := 0.0 + if _scale_start_size.y != 0.0: + scale_y = new_size.y / _scale_start_size.y + var scale_shift := signf(scale_x) * minf(absf(scale_x), absf(scale_y)) + for index in _control_points.size(): + var start_pos := _position_cache[index] + var cp := _control_points[index] + if shift_pressed: + var new_pos := (start_pos - _scale_start_center) * scale_shift + _scale_start_center + cp.set_control_position(new_pos) + else: + var new_pos := (start_pos - _scale_start_center) * Vector2(scale_x, scale_y) + _scale_start_center + cp.set_control_position(new_pos) + else: + var inner_start_position := _scale_start_position + Vector2(12, 12) + var inner_start_size := _scale_start_size - Vector2(24, 24) + var new_size := Vector2(_scale_start_position.x + _scale_start_size.x - mouse_position.x, mouse_position.y - _scale_start_position.y) + var inner_size := new_size + if new_size.x > 24: + inner_size.x -= 24 + elif new_size.x > 0: + inner_size.x = 0 + if new_size.y > 24: + inner_size.y -= 24 + elif new_size.y > 0: + inner_size.y = 0 + var scale_anchor := Vector2(inner_start_position.x + inner_start_size.x, inner_start_position.y) + var scale_x := inner_size.x / inner_start_size.x + var scale_y := inner_size.y / inner_start_size.y + var scale_shift := signf(scale_x) * minf(absf(scale_x), absf(scale_y)) + for index in _control_points.size(): + var start_pos := _position_cache[index] + var cp := _control_points[index] + if shift_pressed: + var new_pos := (start_pos - scale_anchor) * scale_shift + scale_anchor + cp.set_control_position(new_pos) + else: + var new_pos := (start_pos - scale_anchor) * Vector2(scale_x, scale_y) + scale_anchor + cp.set_control_position(new_pos) + _reset_control() + + +## 右上でスケールしたときの挙動。 +func _scale_top_right() -> void: + var mouse_position: Vector2 = (get_parent() as Control).get_local_mouse_position() + var ctrl_pressed := Input.is_key_pressed(KEY_CTRL) + var shift_pressed := Input.is_key_pressed(KEY_SHIFT) + if ctrl_pressed: + var new_size := Vector2((mouse_position.x - _scale_start_center.x) * 2, (_scale_start_center.y - mouse_position.y) * 2) + var scale_x := 0.0 + if _scale_start_size.x != 0.0: + scale_x = new_size.x / _scale_start_size.x + var scale_y := 0.0 + if _scale_start_size.y != 0.0: + scale_y = new_size.y / _scale_start_size.y + var scale_shift := signf(scale_x) * minf(absf(scale_x), absf(scale_y)) + for index in _control_points.size(): + var start_pos := _position_cache[index] + var cp := _control_points[index] + if shift_pressed: + var new_pos := (start_pos - _scale_start_center) * scale_shift + _scale_start_center + cp.set_control_position(new_pos) + else: + var new_pos := (start_pos - _scale_start_center) * Vector2(scale_x, scale_y) + _scale_start_center + cp.set_control_position(new_pos) + else: + var new_size := Vector2(mouse_position.x - _scale_start_position.x, _scale_start_position.y + _scale_start_size.y - mouse_position.y) + var scale_x := 0.0 + if _scale_start_size.x != 0.0: + scale_x = new_size.x / _scale_start_size.x + var scale_y := 0.0 + if _scale_start_size.y != 0.0: + scale_y = new_size.y / _scale_start_size.y + var scale_anchor := Vector2(_scale_start_position.x, _scale_start_position.y + _scale_start_size.y) + var scale_shift := signf(scale_x) * minf(absf(scale_x), absf(scale_y)) + for index in _control_points.size(): + var start_pos := _position_cache[index] + var cp := _control_points[index] + if shift_pressed: + var new_pos := (start_pos - scale_anchor) * scale_shift + scale_anchor + cp.set_control_position(new_pos) + else: + var new_pos := (start_pos - scale_anchor) * Vector2(scale_x, scale_y) + scale_anchor + cp.set_control_position(new_pos) + _reset_control() + + +## 左でスケールしたときの挙動。 +func _scale_left() -> void: + var mouse_position: Vector2 = (get_parent() as Control).get_local_mouse_position() + var ctrl_pressed := Input.is_key_pressed(KEY_CTRL) + var shift_pressed := Input.is_key_pressed(KEY_SHIFT) + if ctrl_pressed: + var new_size_x := (_scale_start_center.x - mouse_position.x) * 2 + var scale_x := 0.0 + if _scale_start_size.x != 0.0: + scale_x = new_size_x / _scale_start_size.x + for index in _control_points.size(): + var start_pos := _position_cache[index] + var cp := _control_points[index] + if shift_pressed: + var new_pos := (start_pos - _scale_start_center) * Vector2(scale_x, scale_x * signf(scale_x)) + _scale_start_center + cp.set_control_position(new_pos) + else: + var new_pos := (start_pos - _scale_start_center) * Vector2(scale_x, 1) + _scale_start_center + cp.set_control_position(new_pos) + else: + var new_size_x := _scale_start_position.x + _scale_start_size.x - mouse_position.x + var scale_x := 0.0 + if _scale_start_size.x != 0.0: + scale_x = new_size_x / _scale_start_size.x + var scale_anchor := Vector2(_scale_start_position.x + _scale_start_size.x, _scale_start_center.y) + for index in _control_points.size(): + var start_pos := _position_cache[index] + var cp := _control_points[index] + if shift_pressed: + var new_pos := (start_pos - scale_anchor) * Vector2(scale_x, scale_x * signf(scale_x)) + scale_anchor + cp.set_control_position(new_pos) + else: + var new_pos := (start_pos - scale_anchor) * Vector2(scale_x, 1) + scale_anchor + cp.set_control_position(new_pos) + _reset_control() + + +## 上でスケールしたときの挙動。 +func _scale_top() -> void: + var mouse_position: Vector2 = (get_parent() as Control).get_local_mouse_position() + var ctrl_pressed := Input.is_key_pressed(KEY_CTRL) + var shift_pressed := Input.is_key_pressed(KEY_SHIFT) + if ctrl_pressed: + var new_size_y := (_scale_start_center.y - mouse_position.y) * 2 + var scale_y := 0.0 + if _scale_start_size.y != 0.0: + scale_y = new_size_y / _scale_start_size.y + for index in _control_points.size(): + var start_pos := _position_cache[index] + var cp := _control_points[index] + if shift_pressed: + var new_pos := (start_pos - _scale_start_center) * Vector2(scale_y * signf(scale_y), scale_y) + _scale_start_center + cp.set_control_position(new_pos) + else: + var new_pos := (start_pos - _scale_start_center) * Vector2(1, scale_y) + _scale_start_center + cp.set_control_position(new_pos) + else: + var new_size_y := _scale_start_position.y + _scale_start_size.y - mouse_position.y + var scale_y := 0.0 + if _scale_start_size.y != 0.0: + scale_y = new_size_y / _scale_start_size.y + var scale_anchor := Vector2(_scale_start_center.x, _scale_start_position.y + _scale_start_size.y) + for index in _control_points.size(): + var start_pos := _position_cache[index] + var cp := _control_points[index] + if shift_pressed: + var new_pos := (start_pos - scale_anchor) * Vector2(scale_y * signf(scale_y), scale_y) + scale_anchor + cp.set_control_position(new_pos) + else: + var new_pos := (start_pos - scale_anchor) * Vector2(1, scale_y) + scale_anchor + cp.set_control_position(new_pos) + _reset_control() + + +## 左上でスケールしたときの挙動。 +func _scale_top_left() -> void: + var mouse_position: Vector2 = (get_parent() as Control).get_local_mouse_position() + var ctrl_pressed := Input.is_key_pressed(KEY_CTRL) + var shift_pressed := Input.is_key_pressed(KEY_SHIFT) + if ctrl_pressed: + var new_size := Vector2((_scale_start_center.x - mouse_position.x) * 2, (_scale_start_center.y - mouse_position.y) * 2) + var scale_x := 0.0 + if _scale_start_size.x != 0.0: + scale_x = new_size.x / _scale_start_size.x + var scale_y := 0.0 + if _scale_start_size.y != 0.0: + scale_y = new_size.y / _scale_start_size.y + var scale_shift := signf(scale_x) * minf(absf(scale_x), absf(scale_y)) + for index in _control_points.size(): + var start_pos := _position_cache[index] + var cp := _control_points[index] + if shift_pressed: + var new_pos := (start_pos - _scale_start_center) * scale_shift + _scale_start_center + cp.set_control_position(new_pos) + else: + var new_pos := (start_pos - _scale_start_center) * Vector2(scale_x, scale_y) + _scale_start_center + cp.set_control_position(new_pos) + else: + var new_size := Vector2(_scale_start_position.x + _scale_start_size.x - mouse_position.x, _scale_start_position.y + _scale_start_size.y - mouse_position.y) + var scale_x := 0.0 + if _scale_start_size.x != 0.0: + scale_x = new_size.x / _scale_start_size.x + var scale_y := 0.0 + if _scale_start_size.y != 0.0: + scale_y = new_size.y / _scale_start_size.y + var scale_anchor := Vector2(_scale_start_position.x + _scale_start_size.x, _scale_start_position.y + _scale_start_size.y) + var scale_shift := signf(scale_x) * minf(absf(scale_x), absf(scale_y)) + for index in _control_points.size(): + var start_pos := _position_cache[index] + var cp := _control_points[index] + if shift_pressed: + var new_pos := (start_pos - scale_anchor) * scale_shift + scale_anchor + cp.set_control_position(new_pos) + else: + var new_pos := (start_pos - scale_anchor) * Vector2(scale_x, scale_y) + scale_anchor + cp.set_control_position(new_pos) + _reset_control() + + +## 回転したときの挙動。 +func _rotate() -> void: + var mouse_position: Vector2 = (get_parent() as Control).get_local_mouse_position() + var angle := (mouse_position.x - _drag_start_position.x) / 200 + for index in _control_points.size(): + var start_pos := _position_cache[index] + var cp := _control_points[index] + var rotate_pos := (start_pos - _rotation_center).rotated(angle) + _rotation_center + cp.set_control_position(rotate_pos) + rotation = angle + + +## _control_pointsの位置をキャプチャする。 +func _capture_control_points_position() -> void: + _position_cache.clear() + for cp in _control_points: + _position_cache.append(cp.get_control_position()) + + +## 現在の_control_pointsに合わせてコントローラーを設置し直す。 +func _reset_control() -> void: + if _control_points.size() <= 1: + visible = false + return + + visible = true + + # _control_pointsのバウンディングボックスを求める + var min_x := _control_points[0].get_control_position().x + var min_y := _control_points[0].get_control_position().y + var max_x := _control_points[0].get_control_position().x + var max_y := _control_points[0].get_control_position().y + for i in range(1, _control_points.size()): + var x := _control_points[i].get_control_position().x + var y := _control_points[i].get_control_position().y + if min_x > x: + min_x = x + elif max_x < x: + max_x = x + if min_y > y: + min_y = y + elif max_y < y: + max_y = y + + # _control_pointsのバウンディングボックスを外に12px拡張したサイズを設定する + var top_left := Vector2(min_x, min_y) + var bottom_right := Vector2(max_x, max_y) + + position = top_left + size = bottom_right - top_left + pivot_offset = size / 2 + rotation = 0 + + +## CPを与えてコントローラーを表示する。 +func set_control_points(control_points: Array[ControlPoint]) -> void: + var dirty := false + if _control_points.size() != control_points.size(): + dirty = true + else: + for index in control_points.size(): + if _control_points[index] != control_points[index]: + dirty = true + break + + if dirty: + _control_points.clear() + _control_points.append_array(control_points) + _reset_control() + + +## このコントローラーがホバー中かどうか。 +func is_hover() -> bool: + return _hover or \ + _top_left_hover or \ + _top_hover or \ + _left_hover or \ + _top_right_hover or \ + _bottom_left_hover or \ + _right_hover or \ + _bottom_hover or \ + _bottom_right_hover or \ + _rotation_hover + + +## このコントローラーを操作中かどうか。 +func is_manipulating() -> bool: + return _mouse_moving + + +## 非表示にする。 +func control_hide() -> void: + _hide = true + + +## 表示する。 +func control_show() -> void: + _hide = false + + +## マウスカーソルがメイン領域に入った際に呼ばれる。 +func _on_mouse_entered() -> void: + _hover = true + + +## マウスカーソルがメイン領域から出た際に呼ばれる。 +func _on_mouse_exited() -> void: + _hover = false + + +## マウスカーソルが左上角領域に入った際に呼ばれる。 +func _on_top_left_control_mouse_entered() -> void: + _top_left_hover = true + + +## マウスカーソルが左上角領域から出た際に呼ばれる。 +func _on_top_left_control_mouse_exited() -> void: + _top_left_hover = false + + +## マウスカーソルが上辺領域に入った際に呼ばれる。 +func _on_top_control_mouse_entered() -> void: + _top_hover = true + + +## マウスカーソルが上辺領域から出た際に呼ばれる。 +func _on_top_control_mouse_exited() -> void: + _top_hover = false + + +## マウスカーソルが左辺領域に入った際に呼ばれる。 +func _on_left_control_mouse_entered() -> void: + _left_hover = true + + +## マウスカーソルが左辺領域から出た際に呼ばれる。 +func _on_left_control_mouse_exited() -> void: + _left_hover = false + + +## マウスカーソルが右上角領域に入った際に呼ばれる。 +func _on_top_right_control_mouse_entered() -> void: + _top_right_hover = true + + +## マウスカーソルが右上角領域から出た際に呼ばれる。 +func _on_top_right_control_mouse_exited() -> void: + _top_right_hover = false + + +## マウスカーソルが左下角領域に入った際に呼ばれる。 +func _on_bottom_left_control_mouse_entered() -> void: + _bottom_left_hover = true + + +## マウスカーソルが左下角領域から出た際に呼ばれる。 +func _on_bottom_left_control_mouse_exited() -> void: + _bottom_left_hover = false + + +## マウスカーソルが右辺領域に入った際に呼ばれる。 +func _on_right_control_mouse_entered() -> void: + _right_hover = true + + +## マウスカーソルが右辺領域から出た際に呼ばれる。 +func _on_right_control_mouse_exited() -> void: + _right_hover = false + + +## マウスカーソルが下辺領域に入った際に呼ばれる。 +func _on_bottom_control_mouse_entered() -> void: + _bottom_hover = true + + +## マウスカーソルが下辺領域から出た際に呼ばれる。 +func _on_bottom_control_mouse_exited() -> void: + _bottom_hover = false + + +## マウスカーソルが右下角領域に入った際に呼ばれる。 +func _on_bottom_right_control_mouse_entered() -> void: + _bottom_right_hover = true + + +## マウスカーソルが右下角領域から出た際に呼ばれる。 +func _on_bottom_right_control_mouse_exited() -> void: + _bottom_right_hover = false + + +## マウスカーソルが回転コントローラーの領域に入った際に呼ばれる。 +func _on_rotation_control_mouse_entered() -> void: + _rotation_hover = true + + +## マウスカーソルが回転コントローラーの領域から出た際に呼ばれる。 +func _on_rotation_control_mouse_exited() -> void: + _rotation_hover = false diff --git a/godot/src/layer/fill_paint_layer.gd b/godot/src/layer/fill_paint_layer.gd new file mode 100644 index 0000000..7b0806d --- /dev/null +++ b/godot/src/layer/fill_paint_layer.gd @@ -0,0 +1,57 @@ +## 塗りつぶしレイヤーを表現するクラス。 +class_name FillPaintLayer +extends PaintLayer + + +## マテリアルmissing時の表示用マテリアル。 +@export var missing_material: ShaderMaterial + +## fillの色。 +var fill_material_id: String = "" + + +## 初期化する。 +func _construct() -> void: + Main.on_change_material_parameters_changed.connect(func() -> void: (self as PaintLayer).need_composite = true) + (self as PaintLayer).need_composite = true + + +## ルートから親の表示状態を更新する。 +func update_parent_visible(new_parent_visible: bool) -> void: + parent_visible = new_parent_visible + + +## ルートから親のロック状態を更新する。 +func update_parent_locked(new_parent_locked: bool) -> void: + parent_locked = new_parent_locked + + +## マテリアルがmissingかどうか。 +func is_material_missing() -> bool: + var fill_material := Main.materials.get_material(fill_material_id) + if fill_material == null: + return true + return false + + +## レイヤーをクリアする。 +func clear_layer() -> void: + clipped = false + visible = true + parent_visible = true + locked = false + parent_locked = false + blend_mode = BlendMode.Normal + alpha = 100 + layer_name = "" + + fill_material_id = "" + + +## レイヤーを作成する。 +static func new_layer() -> FillPaintLayer: + var fill_layer := FillPaintLayer.new() + fill_layer._construct() + fill_layer.layer_name = Main.layers.get_new_layer_name() + fill_layer.fill_material_id = Main.materials.new_fill_layer_material_id + return fill_layer diff --git a/godot/src/layer/group_paint_layer.gd b/godot/src/layer/group_paint_layer.gd new file mode 100644 index 0000000..7c98203 --- /dev/null +++ b/godot/src/layer/group_paint_layer.gd @@ -0,0 +1,83 @@ +## レイヤーグループのレイヤーを表現するクラス。 +class_name GroupPaintLayer +extends PaintLayer + + +## 折りたたまれているか。 +var collapsed: bool = false + +## このグループレイヤーの子要素のレイヤー +var child_layers: Array[PaintLayer] = [] + + +## 初期化する。 +func _construct() -> void: + (self as PaintLayer).need_composite = true + + +## ルートから親の表示状態を更新する。 +func update_parent_visible(new_parent_visible: bool) -> void: + parent_visible = new_parent_visible + + var child_visible := visible and parent_visible + for child_layer in child_layers: + if child_layer is PathPaintLayer: + var path_layer := child_layer as PathPaintLayer + path_layer.update_parent_visible(child_visible) + elif child_layer is FillPaintLayer: + var fill_layer := child_layer as FillPaintLayer + fill_layer.update_parent_visible(child_visible) + elif child_layer is GroupPaintLayer: + var group_layer := child_layer as GroupPaintLayer + group_layer.update_parent_visible(child_visible) + + +## ルートから親のロック状態を更新する。 +func update_parent_locked(new_parent_locked: bool) -> void: + parent_locked = new_parent_locked + + var child_locked := locked or parent_locked + for child_layer in child_layers: + if child_layer is PathPaintLayer: + var path_layer := child_layer as PathPaintLayer + path_layer.update_parent_locked(child_locked) + elif child_layer is FillPaintLayer: + var fill_layer := child_layer as FillPaintLayer + fill_layer.update_parent_locked(child_locked) + elif child_layer is GroupPaintLayer: + var group_layer := child_layer as GroupPaintLayer + group_layer.update_parent_locked(child_locked) + + +## ドキュメントサイズの変更を適用する。 +func change_document_size(new_document_size: Vector2, _anchor: PaintLayer.ScaleAnchor) -> void: + for layer in child_layers: + if layer is PathPaintLayer: + var path_layer := layer as PathPaintLayer + path_layer.change_document_size(new_document_size, _anchor) + elif layer is GroupPaintLayer: + var group_layer := layer as GroupPaintLayer + group_layer.change_document_size(new_document_size, _anchor) + + +## レイヤーをクリアする。 +func clear_layer() -> void: + clipped = false + visible = true + parent_visible = true + locked = false + parent_locked = false + blend_mode = BlendMode.Normal + alpha = 100 + layer_name = "" + + collapsed = false + child_layers = [] + + +## レイヤーを作成する。 +static func new_layer() -> GroupPaintLayer: + var group_layer := GroupPaintLayer.new() + group_layer._construct() + group_layer.layer_name = Main.layers.get_new_layer_name() + return group_layer diff --git a/godot/src/layer/paint_layer.gd b/godot/src/layer/paint_layer.gd new file mode 100644 index 0000000..8c327d6 --- /dev/null +++ b/godot/src/layer/paint_layer.gd @@ -0,0 +1,102 @@ +## レイヤーを表す基底クラス。 +class_name PaintLayer +extends Node + + +const uuid_util = preload('res://addons/uuid/uuid.gd') + +## レイヤーのID。 +var id: String + +## 下のレイヤーでクリップされているかどうか。 +var clipped: bool = false + +## レイヤーが表示されているかどうか。 +var visible: bool = true +## 親のレイヤーが表示されているかどうか。 +var parent_visible: bool = true + +## レイヤーがロックされているかどうか。 +var locked: bool = false +## 親のレイヤーがロックされているかどうか。 +var parent_locked: bool = false + + +## このレイヤーのコンポジットが必要かどうか。 +var need_composite: bool = false +## このレイヤーの親のコンポジットが必要かどうか。 +var need_parent_composite: bool = false +## コンポジットをする場合にオンにするマーク。 +var mark_composite: bool = false + +## レイヤーのブレンドモードの列挙子。 +enum BlendMode { + Normal, + Add, + Multiply, + Screen, + Overlay, +} + +## レイヤーのブレンドモード。 +var blend_mode: BlendMode = BlendMode.Normal + +## alphaの%値 +var alpha: int = 100 + +## レイヤーの名前。 +var layer_name: String = tr("LAYER") + +## レイヤーの拡大縮小の基準の列挙子。 +enum ScaleAnchor { + TopLeft, + TopCenter, + TopRight, + Left, + Center, + Right, + BottomLeft, + BottomCenter, + BottomRight, +} + + +func _init() -> void: + id = uuid_util.v4() + + +## 表示状態を更新する。 +func update_visible(new_visible: bool) -> void: + visible = new_visible + need_parent_composite = true + Main.layers.update_parent_visible() + + +## クリッピング状態を変更する。 +func update_clipped(new_clipped: bool) -> void: + clipped = new_clipped + need_parent_composite = true + + +## ブレンドモードを更新する。 +func update_blend_mode(new_blend_mode: BlendMode) -> void: + blend_mode = new_blend_mode + need_parent_composite = true + + +## アルファを更新する。 +func update_alpha(new_alpha: int) -> void: + alpha = new_alpha + need_parent_composite = true + + +## ロック状態を更新する。 +func update_locked(new_locked: bool) -> void: + locked = new_locked + + +## ロック状態かどうかを返す。 +func is_locked() -> bool: + if locked or parent_locked: + return true + return false diff --git a/godot/src/layer/path.gd b/godot/src/layer/path.gd new file mode 100644 index 0000000..7922d72 --- /dev/null +++ b/godot/src/layer/path.gd @@ -0,0 +1,578 @@ +## レイヤー内に含まれる一つのパスを表現するクラス。 +class_name Path +extends Node + + +const uuid_util = preload('res://addons/uuid/uuid.gd') + + +@onready var _sub_viewport: SubViewport = $"." +@onready var _polygon2d: Polygon2D = $Polygon2D +@onready var _line2d: Line2D = $Line2D + +## マテリアルmissing時の表示用マテリアル。 +@export var missing_material: ShaderMaterial +## 線形グラデーションの表示用マテリアル。 +@export var linear_gradient_material: ShaderMaterial +## 放射グラデーションの表示用マテリアル。 +@export var radial_gradient_material: ShaderMaterial + +## 親のパスレイヤーのWeakRef。 +var parent_path_layer: WeakRef + +## レイヤーのID。 +var id: String + +## レイヤーが表示されているかどうか。 +var visible: bool = true +## 親のレイヤーが表示されているかどうか。 +var parent_visible: bool = true + +## レイヤーがロックされているかどうか。 +var locked: bool = false +## 親のレイヤーがロックされているかどうか。 +var parent_locked: bool = false + +## ControlPointの配列。 +var control_points: PackedVector2Array = PackedVector2Array() +## weightの配列。 +var weights: PackedFloat32Array = PackedFloat32Array() +## phiの配列。 +var phis: PackedFloat32Array = PackedFloat32Array() +## psiの配列。 +var psis: PackedFloat32Array = PackedFloat32Array() +## closed pathかどうか。 +var closed: bool = false + +## パスのポリゴン形状のPackedVector2Array。 +var polygon: PackedVector2Array = PackedVector2Array() +## polygonの頂点列のPackedVector2Array。 +var vertices: PackedVector2Array = PackedVector2Array() +## polygonのindicesのPackedInt32Array。 +var indices: PackedInt32Array = PackedInt32Array() +## パスのサムネ用のindices +var thumbnail_indices: Array[PackedInt32Array] = [] +## polygonの頂点列のPackedVector2Array。 +var line_vertices: PackedVector2Array = PackedVector2Array() +## polygonのindicesのPackedInt32Array。 +var line_indices: PackedInt32Array = PackedInt32Array() +## segmentsのPackedVector2Arrayの配列。 +var segments: Array[PackedVector2Array] = [] + +## パスのブーリアンの列挙子 +enum Boolean { + Union, + Diff, + Intersect, + Xor, +} + +## パスのブーリアンの種類。 +var boolean: Boolean = Boolean.Union + +## パスの名前。 +var path_name: String = "パス" + +const NEW_PHI_VALUE: float = 1.0 +const NEW_PSI_VALUE: float = 0.0 + +## このフレームで再計算を予約したかどうかのフラグ。 +var _need_recalculate_polygon: bool = false + +## コントロールポイントを増減したかどうかのフラグ。 +var _change_controls_count: bool = false + +## レイヤーの線の状態。 +var _is_line: bool = true +## サムネ用マテリアル。 +var _material: PaintMaterial +## 線の太さ。 +var _line_width: float = 2.0 + + +## Pathのシーン。 +static var _path_scene: PackedScene + + +func _init() -> void: + id = uuid_util.v4() + + +func _process(_delta: float) -> void: + if _need_recalculate_polygon: + recalculate_polygon() + _need_recalculate_polygon = false + + +## 単色の塗りつぶしとしてサムネをアップデートする +func _update_fill_thumbnail_color(color: Color) -> void: + _polygon2d.visible = true + _polygon2d.material = null + _polygon2d.color = color + _polygon2d.polygon = vertices + _polygon2d.polygons = thumbnail_indices + _line2d.visible = false + _sub_viewport.render_target_update_mode = SubViewport.UPDATE_ONCE + + +## 単色のラインとしてサムネをアップデートする +func _update_line_thumbnail_color(color: Color) -> void: + _polygon2d.visible = false + _line2d.visible = true + _line2d.material = null + _line2d.default_color = color + _line2d.points = polygon + _sub_viewport.render_target_update_mode = SubViewport.UPDATE_ONCE + + +## 線形グラデーションの塗りつぶしとしてサムネをアップデートする +func _update_fill_thumbnail_linear_gradient(material: LinearGradientPaintMaterial) -> void: + _polygon2d.visible = true + var mat := linear_gradient_material.duplicate() as ShaderMaterial + mat.set_shader_parameter("gradient_texture", material.gradient_texture) + mat.set_shader_parameter("start_point", material.start_point / Main.document_size) + mat.set_shader_parameter("end_point", material.end_point / Main.document_size) + _polygon2d.material = mat + _polygon2d.polygon = vertices + _polygon2d.polygons = thumbnail_indices + _polygon2d.color = Color.WHITE + _line2d.visible = false + _sub_viewport.render_target_update_mode = SubViewport.UPDATE_ONCE + + +## 線形グラデーションのラインとしてサムネをアップデートする +func _update_line_thumbnail_linear_gradient(material: LinearGradientPaintMaterial) -> void: + _polygon2d.visible = false + _line2d.visible = true + var mat := linear_gradient_material.duplicate() as ShaderMaterial + mat.set_shader_parameter("gradient_texture", material.gradient_texture) + mat.set_shader_parameter("start_point", material.start_point / Main.document_size) + mat.set_shader_parameter("end_point", material.end_point / Main.document_size) + _line2d.material = mat + _line2d.points = polygon + _line2d.default_color = Color.WHITE + _sub_viewport.render_target_update_mode = SubViewport.UPDATE_ONCE + + +## 放射グラデーションの塗りつぶしとしてサムネをアップデートする +func _update_fill_thumbnail_radial_gradient(material: RadialGradientPaintMaterial) -> void: + _polygon2d.visible = true + var mat := radial_gradient_material.duplicate() as ShaderMaterial + mat.set_shader_parameter("gradient_texture", material.gradient_texture) + mat.set_shader_parameter("center_point", material.center_point / Main.document_size) + mat.set_shader_parameter("handle_1_point", material.handle_1_point / Main.document_size) + mat.set_shader_parameter("handle_2_point", material.handle_2_point / Main.document_size) + _polygon2d.material = mat + _polygon2d.polygon = vertices + _polygon2d.polygons = thumbnail_indices + _polygon2d.color = Color.WHITE + _line2d.visible = false + _sub_viewport.render_target_update_mode = SubViewport.UPDATE_ONCE + + +## 放射グラデーションのラインとしてサムネをアップデートする +func _update_line_thumbnail_radial_gradient(material: RadialGradientPaintMaterial) -> void: + _polygon2d.visible = false + _line2d.visible = true + var mat := radial_gradient_material.duplicate() as ShaderMaterial + mat.set_shader_parameter("gradient_texture", material.gradient_texture) + mat.set_shader_parameter("center_point", material.center_point / Main.document_size) + mat.set_shader_parameter("handle_1_point", material.handle_1_point / Main.document_size) + mat.set_shader_parameter("handle_2_point", material.handle_2_point / Main.document_size) + _line2d.material = mat + _line2d.points = polygon + _line2d.default_color = Color.WHITE + _sub_viewport.render_target_update_mode = SubViewport.UPDATE_ONCE + + +## マテリアルが存在しない状態で塗りつぶしとしてサムネをアップデートする +func _update_fill_thumbnail_missing() -> void: + _polygon2d.visible = true + _polygon2d.material = missing_material + _polygon2d.polygon = vertices + _polygon2d.polygons = thumbnail_indices + _polygon2d.color = Color.WHITE + _line2d.visible = false + _sub_viewport.render_target_update_mode = SubViewport.UPDATE_ONCE + + +## マテリアルが存在しない状態でラインとしてサムネをアップデートする +func _update_line_thumbnail_missing() -> void: + _polygon2d.visible = false + _line2d.visible = true + _line2d.material = missing_material + _line2d.points = polygon + _line2d.default_color = Color.WHITE + _sub_viewport.render_target_update_mode = SubViewport.UPDATE_ONCE + + +func update_thumbnail() -> void: + if _is_line: + if _material is ColorPaintMaterial: + var color_material := _material as ColorPaintMaterial + _update_line_thumbnail_color(color_material.color) + elif _material is LinearGradientPaintMaterial: + var gradient_material := _material as LinearGradientPaintMaterial + _update_line_thumbnail_linear_gradient(gradient_material) + elif _material is RadialGradientPaintMaterial: + var gradient_material := _material as RadialGradientPaintMaterial + _update_line_thumbnail_radial_gradient(gradient_material) + else: + _update_line_thumbnail_missing() + else: + if _material is ColorPaintMaterial: + var color_material := _material as ColorPaintMaterial + _update_fill_thumbnail_color(color_material.color) + elif _material is LinearGradientPaintMaterial: + var gradient_material := _material as LinearGradientPaintMaterial + _update_fill_thumbnail_linear_gradient(gradient_material) + elif _material is RadialGradientPaintMaterial: + var gradient_material := _material as RadialGradientPaintMaterial + _update_fill_thumbnail_radial_gradient(gradient_material) + else: + _update_fill_thumbnail_missing() + Main.emit_update_layer_list_tab() + + +## polygonの形状の(再)計算。 +func recalculate_polygon() -> void: + if control_points.size() < 2: + polygon = PackedVector2Array() + vertices = PackedVector2Array() + indices = PackedInt32Array() + thumbnail_indices = [] + line_vertices = PackedVector2Array() + line_indices = PackedInt32Array() + segments = [] + elif control_points.size() == 2: + polygon = control_points.duplicate() + vertices = polygon.duplicate() + indices = PackedInt32Array([0, 1]) + thumbnail_indices = [] + + var line_polygons: Array[PackedVector2Array] = Geometry2D.offset_polyline(polygon, _line_width / 2.0, Geometry2D.JOIN_ROUND, Geometry2D.END_ROUND) + var line_triangulate := ShapeUtil.triangulate_polygons_non_zero(line_polygons) + line_vertices = line_triangulate.vertices + line_indices = line_triangulate.indices + + segments = [control_points.duplicate()] + else: + var ppwpath := PPWPath.new() + ppwpath.control_points = control_points + ppwpath.weights = weights + ppwpath.phis = phis + ppwpath.psis = psis + ppwpath.is_closed = closed + + var ppwpoly := PPWCurve.convert(ppwpath) + polygon = ppwpoly.polygon + + var fill_triangulate := ShapeUtil.triangulate(polygon) + vertices = fill_triangulate.vertices + indices = fill_triangulate.indices + + thumbnail_indices = [] + for i in indices.size(): + if i % 3 == 0: + thumbnail_indices.append(PackedInt32Array()) + thumbnail_indices[-1].append(indices[i]) + + var line_polygons: Array[PackedVector2Array] = Geometry2D.offset_polyline(polygon, _line_width / 2.0, Geometry2D.JOIN_ROUND, Geometry2D.END_ROUND) + var line_triangulate := ShapeUtil.triangulate_polygons_non_zero(line_polygons) + line_vertices = line_triangulate.vertices + line_indices = line_triangulate.indices + + segments = ppwpoly.segments + + _request_parent_composite() + + if _change_controls_count: + _change_controls_count = false + Main.emit_path_shape_changed() + + Main.emit_update_control_segments() + + +## 再計算を反映させる。 +func schedule_recalculate() -> void: + # このフレームで再計算の予約をしたフラグをいれる + _need_recalculate_polygon = true + + +## ControlPointの追加。 +func add_control_point(pos: Vector2) -> void: + control_points.append(pos) + weights.append(1.0) + if control_points.size() > 1: + if closed: + phis.remove_at(phis.size() - 1) + phis.append(NEW_PHI_VALUE) + phis.append(NEW_PHI_VALUE) + psis.remove_at(psis.size() - 1) + psis.append(NEW_PSI_VALUE) + psis.append(NEW_PSI_VALUE) + else: + phis.append(NEW_PHI_VALUE) + psis.append(NEW_PSI_VALUE) + if closed: + for i in psis.size(): + psis[i] = 0.0 + else: + for i in psis.size(): + if i == 0: + psis[i] = 2.0 + elif i == psis.size() - 1: + psis[i] = -2.0 + else: + psis[i] = 0.0 + _change_controls_count = true + schedule_recalculate() + + +## ControlPointの挿入。 +func insert_control_point(pos: Vector2, index: int) -> void: + control_points.insert(index, pos) + weights.insert(index, 1.0) + phis.remove_at(index - 1) + phis.insert(index - 1, NEW_PHI_VALUE) + phis.insert(index, NEW_PHI_VALUE) + psis.remove_at(index - 1) + psis.insert(index - 1, NEW_PSI_VALUE) + psis.insert(index, NEW_PSI_VALUE) + if not closed and index == 1: + psis[0] = 2.0 + if not closed and index == psis.size() - 1: + psis[psis.size() - 1] = -2.0 + _change_controls_count = true + schedule_recalculate() + + +## ControlPointの除去。 +func delete_control_point(index: int) -> void: + if index == 0 or index == 1: + control_points.remove_at(index) + weights.remove_at(index) + phis.remove_at(0) + psis.remove_at(0) + # openの場合最初のpsiを端点のものに上書き + if not closed: + psis[0] = 2.0 + else: + control_points.remove_at(index) + weights.remove_at(index) + phis.remove_at(index - 1) + psis.remove_at(index - 1) + # openで最後もしくは最後から2番目を消した場合にpsiを端点のものに上書き + if not closed and (index == psis.size() or index - 1 == psis.size()): + psis[psis.size() - 1] = -2.0 + _change_controls_count = true + schedule_recalculate() + + +## ControlPointの移動。 +func move_control_point(pos: Vector2, index: int) -> void: + if not control_points[index].is_equal_approx(pos): + control_points[index] = pos + schedule_recalculate() + + +## weightの変更。 +func change_weight(weight: float, index: int) -> void: + if not weights[index] == weight: + weights[index] = weight + schedule_recalculate() + + +## phiの変更。 +func change_phi(phi: float, index: int) -> void: + if not phis[index] == phi: + phis[index] = phi + schedule_recalculate() + + +## psiの変更。 +func change_psi(psi: float, index: int) -> void: + if not psis[index] == psi: + psis[index] = psi + schedule_recalculate() + + +## open/closedの変更。 +func change_closed(is_close: bool) -> void: + if is_close and not closed: + # closedに変化したときには最初と最後のpsiの値を0にリセットしてからつなげる + if psis.size() > 1: + psis[0] = 0.0 + psis[psis.size() - 1] = 0.0 + + phis.append(1.0) + psis.append(0.0) + elif not is_close and closed: + phis.remove_at(phis.size() - 1) + psis.remove_at(psis.size() - 1) + + # openに変化したときは最初と最後のパスのpsiを2.0と-2.0にする + if psis.size() > 1: + psis[0] = 2.0 + psis[psis.size() - 1] = -2.0 + closed = is_close + _change_controls_count = true + schedule_recalculate() + + +## booleanの変更 +func change_boolean(new_boolean: Boolean) -> void: + boolean = new_boolean + _request_parent_composite() + + +## 表示状態を更新する +func update_visible(new_visible: bool) -> void: + visible = new_visible + Main.layers.update_parent_visible.call_deferred() + _request_parent_composite() + + +## ロック状態を更新する。 +func update_locked(new_locked: bool) -> void: + locked = new_locked + + +## ロック状態かどうかを返す。 +func is_locked() -> bool: + if locked or parent_locked: + return true + return false + + +## 線の太さを設定する。 +func set_line_width(line_width: float) -> void: + _line_width = line_width + _line2d.width = line_width + + +## 線のポリゴン形状を再計算する。 +func update_line_polygon() -> void: + var line_polygons: Array[PackedVector2Array] = Geometry2D.offset_polyline(polygon, _line_width / 2.0, Geometry2D.JOIN_ROUND, Geometry2D.END_ROUND) + var line_triangulate := ShapeUtil.triangulate_polygons_non_zero(line_polygons) + line_vertices = line_triangulate.vertices + line_indices = line_triangulate.indices + + +## このパスが線か塗りかを指定する。 +func set_is_line(is_line: bool) -> void: + _is_line = is_line + + +## このパスが線かどうかを取得する。 +func get_is_line() -> bool: + return _is_line + + +func set_material(material: PaintMaterial) -> void: + _material = material + + +## SubViewportのテクスチャを取得する。 +func get_path_texture() -> Texture2D: + return _sub_viewport.get_texture() + + +## ドキュメントサイズの変更を適用する。 +func change_document_size(new_document_size: Vector2, anchor: PaintLayer.ScaleAnchor) -> void: + var prev_document_size := Main.document_size + + # サムネ + if new_document_size.x > new_document_size.y: + _sub_viewport.size = Vector2(38, 38 * new_document_size.y / new_document_size.x) + else: + _sub_viewport.size = Vector2(38 * new_document_size.x / new_document_size.y, 38) + _sub_viewport.size_2d_override = new_document_size + _sub_viewport.size_2d_override_stretch = true + + # パス + var delta_x := 0.0 + var delta_y := 0.0 + match anchor: + PaintLayer.ScaleAnchor.TopLeft: + pass + PaintLayer.ScaleAnchor.TopCenter: + delta_x = (new_document_size.x - prev_document_size.x) / 2 + PaintLayer.ScaleAnchor.TopRight: + delta_x = new_document_size.x - prev_document_size.x + PaintLayer.ScaleAnchor.Left: + delta_y = (new_document_size.y - prev_document_size.y) / 2 + PaintLayer.ScaleAnchor.Center: + delta_x = (new_document_size.x - prev_document_size.x) / 2 + delta_y = (new_document_size.y - prev_document_size.y) / 2 + PaintLayer.ScaleAnchor.Right: + delta_x = new_document_size.x - prev_document_size.x + delta_y = (new_document_size.y - prev_document_size.y) / 2 + PaintLayer.ScaleAnchor.BottomLeft: + delta_y = new_document_size.y - prev_document_size.y + PaintLayer.ScaleAnchor.BottomCenter: + delta_x = (new_document_size.x - prev_document_size.x) / 2 + delta_y = new_document_size.y - prev_document_size.y + PaintLayer.ScaleAnchor.BottomRight: + delta_x = new_document_size.x - prev_document_size.x + delta_y = new_document_size.y - prev_document_size.y + + for i in control_points.size(): + control_points[i] += Vector2(delta_x, delta_y) + + for i in polygon.size(): + polygon[i] += Vector2(delta_x, delta_y) + for i in vertices.size(): + vertices[i] += Vector2(delta_x, delta_y) + for i in line_vertices.size(): + line_vertices[i] += Vector2(delta_x, delta_y) + for i in segments.size(): + for j in segments[i].size(): + segments[i][j] += Vector2(delta_x, delta_y) + + +## 親のcompositeを要求する。 +func _request_parent_composite() -> void: + @warning_ignore("unsafe_cast") + var parent := parent_path_layer.get_ref() as PathPaintLayer + if parent: + parent.set_need_composite() + + +## パスを初期化する +func clear_path() -> void: + parent_path_layer = null + id = uuid_util.v4() + visible = true + locked = false + control_points = PackedVector2Array() + weights = PackedFloat32Array() + phis = PackedFloat32Array() + psis = PackedFloat32Array() + closed = false + polygon = PackedVector2Array() + vertices = PackedVector2Array() + indices = PackedInt32Array() + thumbnail_indices = [] + line_vertices = PackedVector2Array() + line_indices = PackedInt32Array() + segments = [] + boolean = Boolean.Union + path_name = "" + + +## パスを作成する。 +static func new_path(use_new_path_name: bool = true) -> Path: + if not _path_scene: + _path_scene = load("res://scenes/node/path.tscn") + var path := _path_scene.instantiate() as Path + path.change_document_size.call_deferred(Main.document_size, PaintLayer.ScaleAnchor.Center) + + if use_new_path_name: + path.path_name = Main.layers.get_new_path_name() + + var scene_tree := Engine.get_main_loop() as SceneTree + var root := scene_tree.root + var sub_viewport_root := root.get_node("/root/App/LayerSubViewports") + sub_viewport_root.add_child(path) + return path diff --git a/godot/src/layer/path_paint_layer.gd b/godot/src/layer/path_paint_layer.gd new file mode 100644 index 0000000..f7eb9c2 --- /dev/null +++ b/godot/src/layer/path_paint_layer.gd @@ -0,0 +1,223 @@ +## パスをまとめたレイヤーを表現するクラス。 +class_name PathPaintLayer +extends PaintLayer + + +## マテリアルmissing時の表示用マテリアル。 +@export var missing_material: ShaderMaterial +## boolean描画用のマテリアル。 +@export var boolean_draw_material: ShaderMaterial + +## fillするかどうか。 +var filled: bool = true: + get: return filled + set(value): + filled = value + set_need_composite() +## fillの色。 +var fill_material_id: String = "": + get: return fill_material_id + set(value): + fill_material_id = value + set_need_composite() +## outlineのオンオフ。 +var outlined: bool = true: + get: return outlined + set(value): + outlined = value + set_need_composite() +## outlineの色。 +var outline_material_id: String = "": + get: return outline_material_id + set(value): + outline_material_id = value + set_need_composite() +## outlineの幅。 +var outline_width: float = 2.0: + get: return outline_width + set(value): + outline_width = value + for path in paths: + path.set_line_width(outline_width) + path.update_line_polygon() + set_need_composite() + +## 折りたたまれているか。 +var collapsed: bool = false + +## レイヤーの保持するパスの配列。 +var paths: Array[Path] = [] + +## 子要素のサムネをアップデートするかどうか。 +var _need_update_children_thumbnaisl: bool = false + + +func _process(_delta: float) -> void: + if _need_update_children_thumbnaisl: + update_child_paths_thumbnail() + + +## 初期化する。 +func _construct() -> void: + Main.on_change_material_parameters_changed.connect(func() -> void: set_need_composite()) + set_need_composite() + + +## ルートから親の表示状態を更新する。 +func update_parent_visible(new_parent_visible: bool) -> void: + parent_visible = new_parent_visible + + var child_visible := visible and parent_visible + for path in paths: + path.parent_visible = child_visible + + +## ルートから親のロック状態を更新する。 +func update_parent_locked(new_parent_locked: bool) -> void: + parent_locked = new_parent_locked + + var child_locked := locked or parent_locked + for path in paths: + path.parent_locked = child_locked + + +## レイヤーの形状を計算する。 +func set_need_composite() -> void: + (self as PaintLayer).need_composite = true + _need_update_children_thumbnaisl = true + + +## マテリアルがmissingかどうか。 +func is_material_missing() -> bool: + var fill_material := Main.materials.get_material(fill_material_id) + if fill_material == null: + return true + var line_material := Main.materials.get_material(outline_material_id) + if line_material == null: + return true + return false + + +## このレイヤーにパスを追加する。 +func add_path(use_new_path_name: bool = true) -> Path: + var path := Main.path_pool.get_path(use_new_path_name) + path.set_line_width(outline_width) + path.parent_path_layer = weakref(self) + paths.append(path) + set_need_composite() + return path + + +## このレイヤーにパスを追加する。 +## その際に特定のパスの後ろに追加する。 +## after_pathがこのパスレイヤーに属するパスではない場合は末尾にパスを追加する。 +func add_insert_path_after_path(after_path: Path) -> Path: + var path := Main.path_pool.get_path() + path.set_line_width(outline_width) + path.parent_path_layer = weakref(self) + var insert_index := 0 + for p in paths: + insert_index += 1 + if p == after_path: + break ; + paths.insert(insert_index, path) + set_need_composite() + return path + + +## パスをこのレイヤーから除去する。 +## このレイヤーに含まれていないPathを渡したときは何もしない。 +func remove_path(path: Path) -> void: + for index in paths.size(): + var p := paths[index] + if p == path: + paths.remove_at(index) + (self as PaintLayer).need_composite = true + _need_update_children_thumbnaisl = true + break + + +## パスがこのレイヤーに含まれているか確認する。 +func contain_path(path: Path) -> bool: + for index in paths.size(): + var p := paths[index] + if p == path: + return true + return false + + +## 子要素のパスのサムネイルを更新する。 +func update_child_paths_thumbnail() -> void: + # 子要素のパスのサムネのsub viewportを更新する + if filled: + var fill_material := Main.materials.get_material(fill_material_id) + for path in paths: + path.set_is_line(false) + path.set_material(fill_material) + path.update_thumbnail() + elif outlined: + var line_material := Main.materials.get_material(outline_material_id) + for path in paths: + path.set_is_line(true) + path.set_material(line_material) + path.update_thumbnail() + else: + var fill_material := Main.materials.get_material(fill_material_id) + for path in paths: + path.set_is_line(false) + path.set_material(fill_material) + path.update_thumbnail() + _need_update_children_thumbnaisl = false + + +## 線幅を更新する。 +func update_outline_polygon() -> void: + for path in paths: + path.update_line_polygon() + + +## ドキュメントサイズの変更を適用する。 +func change_document_size(new_document_size: Vector2, anchor: PaintLayer.ScaleAnchor) -> void: + for path in paths: + path.change_document_size(new_document_size, anchor) + + +## レイヤーをクリアする。 +func clear_layer() -> void: + for path in paths: + Main.path_pool.free_path(path) + + clipped = false + visible = true + parent_visible = true + locked = false + parent_locked = false + blend_mode = BlendMode.Normal + alpha = 100 + layer_name = "" + + filled = true + fill_material_id = "" + outlined = true + outline_material_id = "" + outline_width = 2.0 + collapsed = false + paths = [] + + set_need_composite() + + +## レイヤーを作成する。 +static func new_layer() -> PathPaintLayer: + var path_layer := PathPaintLayer.new() + path_layer._construct() + path_layer.fill_material_id = Main.materials.new_path_layer_fill_material_id + path_layer.outline_material_id = Main.materials.new_path_layer_line_material_id + + path_layer.layer_name = Main.layers.get_new_layer_name() + + var scene_tree := Engine.get_main_loop() as SceneTree + var root := scene_tree.root + var sub_viewport_root := root.get_node("/root/App/LayerSubViewports") + sub_viewport_root.add_child(path_layer) + return path_layer diff --git a/godot/src/main/copy_manager.gd b/godot/src/main/copy_manager.gd new file mode 100644 index 0000000..89d279b --- /dev/null +++ b/godot/src/main/copy_manager.gd @@ -0,0 +1,266 @@ +class_name CopyManager +extends RefCounted + + +## コピーしたレイヤー。 +var _copied_layers: Array[PaintLayer] = [] +## コピーしたパス。 +var _copied_paths: Array[Path] = [] +## コピーしたマテリアル。 +var _copied_material: MaterialList.PaintMaterialListItem = null + + +## 選択中のデータをコピーする。 +func copy() -> void: + clear() + if Main.layers.selected_layers.size() > 0: + _copy_layers() + elif Main.layers.selected_paths.size() > 0: + _copy_paths() + elif Main.materials.selected_index != -1: + _copy_material() + + +## レイヤーを複製して保持する。 +func _copy_layers() -> void: + var selected_layers: Array[PaintLayer] = Main.layers._get_selected_path_layers() + + # 選択中のレイヤーのうち、ルート側のものを取得 + var selected_root_layers: Array[PaintLayer] = [] + for layer in selected_layers: + var has_parent := false + for l in selected_layers: + if l is GroupPaintLayer: + var gl := l as GroupPaintLayer + for ll in gl.child_layers: + if ll == layer: + has_parent = true + if not has_parent: + selected_root_layers.append(layer) + + for selected_layer in selected_root_layers: + if selected_layer is PathPaintLayer: + var selected_path_layer := selected_layer as PathPaintLayer + _copied_layers.append(_duplicate_path_layer(selected_path_layer, false)) + elif selected_layer is FillPaintLayer: + var selected_fill_layer := selected_layer as FillPaintLayer + _copied_layers.append(_duplicate_fill_layer(selected_fill_layer, false)) + elif selected_layer is GroupPaintLayer: + var selected_group_layer := selected_layer as GroupPaintLayer + _copied_layers.append(_duplicate_group_layer(selected_group_layer, false)) + + +func _duplicate_path_layer(path_layer: PathPaintLayer, add_suffix: bool) -> PathPaintLayer: + var new_path_layer := Main.layer_pool.get_path_layer() + + if add_suffix: + new_path_layer.layer_name = path_layer.layer_name + " (" + tr("COPY") + ")" + else: + new_path_layer.layer_name = path_layer.layer_name + + new_path_layer.clipped = path_layer.clipped + new_path_layer.visible = path_layer.visible + new_path_layer.locked = path_layer.locked + new_path_layer.blend_mode = path_layer.blend_mode + new_path_layer.alpha = path_layer.alpha + + new_path_layer.filled = path_layer.filled + new_path_layer.fill_material_id = path_layer.fill_material_id + new_path_layer.outlined = path_layer.outlined + new_path_layer.outline_material_id = path_layer.outline_material_id + new_path_layer.outline_width = path_layer.outline_width + new_path_layer.collapsed = path_layer.collapsed + + for path in path_layer.paths: + var new_path := new_path_layer.add_path() + + if add_suffix: + new_path.path_name = path.path_name + " (" + tr("COPY") + ")" + else: + new_path.path_name = path.path_name + + new_path.visible = path.visible + new_path.locked = path.locked + new_path.control_points = path.control_points.duplicate() + new_path.weights = path.weights.duplicate() + new_path.phis = path.phis.duplicate() + new_path.psis = path.psis.duplicate() + new_path.closed = path.closed + new_path.polygon = path.polygon.duplicate() + new_path.vertices = path.vertices.duplicate() + new_path.indices = path.indices.duplicate() + new_path.thumbnail_indices = path.thumbnail_indices.duplicate() + new_path.line_vertices = path.line_vertices.duplicate() + new_path.line_indices = path.line_indices.duplicate() + new_path.segments = path.segments.duplicate() + new_path.boolean = path.boolean + + return new_path_layer + + +func _duplicate_fill_layer(fill_layer: FillPaintLayer, add_suffix: bool) -> FillPaintLayer: + var new_fill_layer := Main.layer_pool.get_fill_layer() + + if add_suffix: + new_fill_layer.layer_name = fill_layer.layer_name + " (" + tr("COPY") + ")" + else: + new_fill_layer.layer_name = fill_layer.layer_name + + new_fill_layer.clipped = fill_layer.clipped + new_fill_layer.visible = fill_layer.visible + new_fill_layer.locked = fill_layer.locked + new_fill_layer.blend_mode = fill_layer.blend_mode + new_fill_layer.alpha = fill_layer.alpha + + new_fill_layer.fill_material_id = fill_layer.fill_material_id + + return new_fill_layer + + +func _duplicate_group_layer(group_layer: GroupPaintLayer, add_suffix: bool) -> GroupPaintLayer: + var new_group_layer := Main.layer_pool.get_group_layer() + + if add_suffix: + new_group_layer.layer_name = group_layer.layer_name + " (" + tr("COPY") + ")" + else: + new_group_layer.layer_name = group_layer.layer_name + + new_group_layer.clipped = group_layer.clipped + new_group_layer.visible = group_layer.visible + new_group_layer.locked = group_layer.locked + new_group_layer.blend_mode = group_layer.blend_mode + new_group_layer.alpha = group_layer.alpha + + new_group_layer.collapsed = group_layer.collapsed + + for child_layer in group_layer.child_layers: + if child_layer is PathPaintLayer: + var child_path_layer := child_layer as PathPaintLayer + new_group_layer.child_layers.append(_duplicate_path_layer(child_path_layer, add_suffix)) + elif child_layer is FillPaintLayer: + var child_fill_layer := child_layer as FillPaintLayer + new_group_layer.child_layers.append(_duplicate_fill_layer(child_fill_layer, add_suffix)) + elif child_layer is GroupPaintLayer: + var child_group_layer := child_layer as GroupPaintLayer + new_group_layer.child_layers.append(_duplicate_group_layer(child_group_layer, add_suffix)) + + return new_group_layer + + +## パスを複製して保持する。 +func _copy_paths() -> void: + for path in Main.layers.selected_paths: + var new_path := _duplicate_path(path) + _copied_paths.append(new_path) + + +## パスを複製する。 +func _duplicate_path(path: Path) -> Path: + var new_path := Main.path_pool.get_path() + + new_path.path_name = path.path_name + new_path.visible = path.visible + new_path.locked = path.locked + new_path.control_points = path.control_points.duplicate() + new_path.weights = path.weights.duplicate() + new_path.phis = path.phis.duplicate() + new_path.psis = path.psis.duplicate() + new_path.closed = path.closed + new_path.polygon = path.polygon.duplicate() + new_path.vertices = path.vertices.duplicate() + new_path.indices = path.indices.duplicate() + new_path.thumbnail_indices = path.thumbnail_indices.duplicate() + new_path.line_vertices = path.line_vertices.duplicate() + new_path.line_indices = path.line_indices.duplicate() + new_path.segments = path.segments.duplicate() + new_path.boolean = path.boolean + + return new_path + + +## マテリアルを複製して保持する。 +func _copy_material() -> void: + var selected_material := Main.materials.list[Main.materials.selected_index] + + var new_material := MaterialList.PaintMaterialListItem.new() + new_material.name = selected_material.name + " (" + tr("COPY") + ")" + + if selected_material.material is ColorPaintMaterial: + var selected_color_material := selected_material.material as ColorPaintMaterial + var color_material := ColorPaintMaterial.new() + color_material.color = selected_color_material.color + new_material.material = color_material + print("color material copied") + + _copied_material = new_material + + +## コピーのために保持しているデータの破棄をする。 +func clear() -> void: + for layer in _copied_layers: + Main.layer_pool.free_layer(layer) + _copied_layers = [] + for path in _copied_paths: + Main.path_pool.free_path(path) + _copied_paths = [] + _copied_material = null + + +## コピーされたレイヤーのリストを複製して返す。 +func get_copied_layers() -> Array[PaintLayer]: + var new_layers: Array[PaintLayer] = [] + + for layer in _copied_layers: + if layer is PathPaintLayer: + var path_layer := layer as PathPaintLayer + new_layers.append(_duplicate_path_layer(path_layer, true)) + elif layer is FillPaintLayer: + var fill_layer := layer as FillPaintLayer + new_layers.append(_duplicate_fill_layer(fill_layer, true)) + elif layer is GroupPaintLayer: + var group_layer := layer as GroupPaintLayer + new_layers.append(_duplicate_group_layer(group_layer, true)) + + return new_layers + + +## コピーされたパスのリストを複製して返す。 +func get_copied_paths() -> Array[Path]: + var new_paths: Array[Path] = [] + + for path in _copied_paths: + var new_path := _duplicate_path(path) + new_path.path_name += " (" + tr("COPY") + ")" + new_paths.append(new_path) + + return new_paths + + +## コピーされたマテリアルを複製して返す。 +func get_copied_material() -> MaterialList.PaintMaterialListItem: + if _copied_material == null: + return null + + var new_material := MaterialList.PaintMaterialListItem.new() + new_material.name = _copied_material.name + + if _copied_material.material is ColorPaintMaterial: + var copied_color_material := _copied_material.material as ColorPaintMaterial + var color_material := ColorPaintMaterial.new() + color_material.color = copied_color_material.color + new_material.material = color_material + + return new_material + + +## ドキュメントサイズ変更に対応する。 +func change_document_size(new_document_size: Vector2, anchor: PaintLayer.ScaleAnchor) -> void: + for layer in _copied_layers: + if layer is PathPaintLayer: + var path_layer := layer as PathPaintLayer + path_layer.change_document_size(new_document_size, anchor) + elif layer is GroupPaintLayer: + var group_layer := layer as GroupPaintLayer + group_layer.change_document_size(new_document_size, anchor) + for path in _copied_paths: + path.change_document_size(new_document_size, anchor) diff --git a/godot/src/main/main.gd b/godot/src/main/main.gd new file mode 100644 index 0000000..fa0ac5a --- /dev/null +++ b/godot/src/main/main.gd @@ -0,0 +1,327 @@ +## メインの処理を書くシングルトンのクラス。 +extends Node + + +## ドキュメントが開かれたときに発火されるシグナル。 +signal on_document_open() +## ドキュメントが閉じたときに発火されるシグナル。 +signal on_document_close() +## ドキュメントをリロードしたときに発火されるシグナル。 +signal on_document_reload() +## ドキュメントのdirtyに変更があったときに発火されるシグナル。 +signal on_document_dirty_changed() +## ミラー状態の変更があったときに発火されるシグナル。 +signal on_mirror_changed() +## 線形グラデーションの編集ボタンが押されたときに発火されるシグナル。 +signal on_start_edit_linear_gradient(gradient_material: LinearGradientPaintMaterial) +## 放射グラデーションの編集ボタンが押されたときに発火されるシグナル。 +signal on_start_edit_radial_gradient(gradient_material: RadialGradientPaintMaterial) +## グラデーションの編集が終わったときに発火されるシグナル。 +signal on_end_edit_gradient() +## マテリアルリストタブの更新が必要な際に発火されるシグナル。 +signal on_update_material_list_tab() +## マテリアルのパラメータに変更が加わったときに呼び出されるシグナル。 +signal on_change_material_parameters_changed() +## レイヤーリストタブの更新が必要な際に発火されるシグナル。 +signal on_update_layer_list_tab() +## 選択中のパスが変更されたときに発火されるシグナル。 +signal on_selected_paths_changed() +## コントロールセグメントの更新が必要な際に発火されるシグナル。 +signal on_update_control_segments() +## パスの形状が変更されたときに発火されるシグナル。 +signal on_path_shape_changed() +## ロケールが変更されたときに発火されるシグナル。 +signal on_locale_changed() + +## ドキュメントが開かれているかどうか。 +var document_opened: bool = false + +## ドキュメントのパス。 +var document_file_path: String + +## ドキュメントに未保存の変更があるかどうか。 +var document_dirty: bool = false: + set(value): + document_dirty = value + on_document_dirty_changed.emit() + get: + return document_dirty + +## ドキュメントのサイズ。 +var document_size: Vector2 = Vector2(480, 720) + +## マテリアルのリスト。 +var materials: MaterialList = MaterialList.new() +## レイヤーのリスト。 +var layers: PaintLayerList = PaintLayerList.new() +## レイヤーのプール。 +var layer_pool: PaintLayerPool = PaintLayerPool.new() +## Pathのプール。 +var path_pool: PathPool = PathPool.new() + +## コピー舌アイテムの管理者。 +var copy_manager: CopyManager = CopyManager.new() + +## メインのコンポジター。 +var compositor := PaintCompositor.new() + +## 現在のヒストリ。 +var history_current: Document = null +## アンドゥのヒストリのリスト。 +var history_undo_list: Array[Document] = [] +## リドゥのヒストリのリスト。 +var history_redo_list: Array[Document] = [] + +## コントローラー、カラーパレットなどを操作中かどうかのフラグをグローバルに置く。 +var is_manipulating: bool: + get: + var scene_tree := Engine.get_main_loop() as SceneTree + var root := scene_tree.root + var app := root.get_node("/root/App") as App + return app.get_is_manipulating() + +## ビューポートの拡大率。 +var viewport_scale: float = 1 + +## 表示のミラー状態。 +var mirror: bool = false: + set(value): + mirror = value + compositor.need_composite = true + on_mirror_changed.emit() + get: + return mirror + +## 自動保存が有効かどうか。 +var auto_save_enabled: bool = false +## 自動保存の間隔。 +var auto_save_interval: int = 5 * 60 +## 最後に自動保存した時刻。 +var auto_save_last_time: float = Time.get_unix_time_from_system() + + +func _ready() -> void: + var config := ConfigFile.new() + var ret := config.load("user://config.cfg") + if ret == OK: + if config.has_section_key("language", "locale"): + @warning_ignore("unsafe_cast") + var local := config.get_value("language", "locale") as String + TranslationServer.set_locale(local) + if config.has_section_key("auto_save", "enabled"): + auto_save_enabled = config.get_value("auto_save", "enabled") + if config.has_section_key("auto_save", "interval"): + auto_save_interval = config.get_value("auto_save", "interval") + + +func _process(_delta: float) -> void: + # ドキュメントが開かれている場合、compositorのテクスチャ生成を更新する。 + if document_opened: + compositor.step_create_texture() + + # 自動保存が有効で、一定時間経過していたら自動保存する。 + if auto_save_enabled: + var current_time := Time.get_unix_time_from_system() + if current_time - auto_save_last_time > auto_save_interval: + var file := FileAccess.open(document_file_path + "1", FileAccess.WRITE) + var document := DocumentSerializer.document_store(true) + file.store_string(var_to_str(document)) + file.close() + + auto_save_last_time = current_time + + +## 線形グラデーションの編集ボタンを押したときに呼び出す関数。 +func emit_start_edit_linear_gradient(gradient_material: LinearGradientPaintMaterial) -> void: + on_start_edit_linear_gradient.emit(gradient_material) + + +## 放射グラデーションの編集ボタンを押したときに呼び出す関数。 +func emit_start_edit_radial_gradient(gradient_material: RadialGradientPaintMaterial) -> void: + on_start_edit_radial_gradient.emit(gradient_material) + + +## グラデーションの編集が終わったときに呼び出す関数。 +func emit_end_edit_gradient() -> void: + on_end_edit_gradient.emit() + + +## マテリアルリストタブの更新が必要なときに呼び出す関数。 +func emit_update_material_list_tab() -> void: + on_update_material_list_tab.emit() + + +## マテリアルのパラメータに変更が加わったときに呼び出す関数。 +func emit_change_material_parameters_changed() -> void: + on_change_material_parameters_changed.emit() + + +## レイヤーリストタブの更新が必要なときに呼び出す関数。 +func emit_update_layer_list_tab() -> void: + on_update_layer_list_tab.emit() + + +## 選択中のパスが変更されたときに呼び出す関数。 +func emit_selected_paths_changed() -> void: + on_selected_paths_changed.emit() + + +## コントロールセグメントの更新が必要なときに呼び出す関数。 +func emit_update_control_segments() -> void: + on_update_control_segments.emit() + + +## パスの形状が変更されたときに呼び出す関数。 +func emit_path_shape_changed() -> void: + on_path_shape_changed.emit() + + +## ロケールが変更されたときに呼び出す関数。 +func emit_locale_changed() -> void: + on_locale_changed.emit() + + +## ドキュメントを新規作成する。 +func create_document(new_document_size: Vector2, file_path: String) -> void: + close_document() + compositor.resize(new_document_size) + document_file_path = file_path + document_opened = true + document_dirty = false + history_undo_list.clear() + history_redo_list.clear() + materials.create_materials_for_new_documet() + layers.new_document(new_document_size) + mirror = false + + history_current = DocumentSerializer.document_store(false) + save_document() + + auto_save_last_time = Time.get_unix_time_from_system() + on_document_open.emit() + on_update_layer_list_tab.emit() + + +## ドキュメントを上書き保存する。 +func save_document() -> void: + if not document_opened: + return + var file := FileAccess.open(document_file_path, FileAccess.WRITE) + var document := DocumentSerializer.document_store(true) + file.store_string(var_to_str(document)) + file.close() + document_dirty = false + + +## ドキュメントを別名で保存する。 +func save_as_document(file_path: String) -> void: + if not document_opened: + return + document_file_path = file_path + var file := FileAccess.open(document_file_path, FileAccess.WRITE) + var document := DocumentSerializer.document_store(true) + file.store_string(var_to_str(document)) + file.close() + document_dirty = false + + +## ドキュメントを読み込む。 +func load_document(file_path: String) -> void: + close_document() + var file := FileAccess.open(file_path, FileAccess.READ) + @warning_ignore("unsafe_cast") + var document := str_to_var(file.get_as_text()) as Document + file.close() + if document: + history_undo_list.clear() + history_redo_list.clear() + document_file_path = file_path + DocumentSerializer.document_load(document, true) + document_dirty = false + history_current = DocumentSerializer.document_store(false) + mirror = false + + auto_save_last_time = Time.get_unix_time_from_system() + on_document_open.emit() + on_update_layer_list_tab.emit() + + +## ドキュメントを閉じる。 +func close_document() -> void: + document_file_path = "" + history_current = null + history_undo_list.clear() + history_redo_list.clear() + materials.clear() + layers.clear() + document_opened = false + document_dirty = false + mirror = false + on_document_close.emit() + on_update_layer_list_tab.emit() + + +## 操作をヒストリに積む。 +func commit_history() -> void: + # パスの計算を反映させるために2フレーム待機する。 + # パスに変更があった場合、1フレーム後にパスの再計算が予約されるので、 + # その結果にアクセスするのは2フレーム後とする。 + await (Engine.get_main_loop() as SceneTree).process_frame + await (Engine.get_main_loop() as SceneTree).process_frame + + history_redo_list.clear() + history_undo_list.append(history_current) + history_current = DocumentSerializer.document_store(false) + # 履歴は無限にメモリを使わないために適当に上限最大1024でキャップする + while history_undo_list.size() > 1024: + history_undo_list.remove_at(0) + document_dirty = true + + +## アンドゥする。 +func undo_hisotry() -> void: + if history_undo_list.size() == 0: + return + history_redo_list.append(history_current) + history_current = history_undo_list.pop_back() + DocumentSerializer.document_load(history_current, false) + document_dirty = true + on_document_reload.emit() + on_update_layer_list_tab.emit() + + +## リドゥする。 +func redo_history() -> void: + if history_redo_list.size() == 0: + return + history_undo_list.append(history_current) + history_current = history_redo_list.pop_back() + DocumentSerializer.document_load(history_current, false) + document_dirty = true + on_document_reload.emit() + on_update_layer_list_tab.emit() + + +## documentをpngで書き出す。 +func export_document(file_path: String, save_document_size: Vector2) -> void: + var save_compositor := PaintCompositor.new() + if save_document_size.x > document_size.x: + save_compositor.resize(save_document_size) + else: + save_compositor.resize(document_size) + + save_compositor.update() + save_compositor.step_create_texture() + + # compositが成功するまでフレームを進める + while not save_compositor.composite(layers.root, true): + await (Engine.get_main_loop() as SceneTree).process_frame + save_compositor.step_create_texture() + + var texture := save_compositor.root_composite_texture.texture + var image := texture.get_image() + image.resize(int(save_document_size.x), int(save_document_size.y), Image.INTERPOLATE_LANCZOS) + image.save_png(file_path) + + compositor.update() + compositor.composite(layers.root, true) diff --git a/godot/src/main/material_list.gd b/godot/src/main/material_list.gd new file mode 100644 index 0000000..6086343 --- /dev/null +++ b/godot/src/main/material_list.gd @@ -0,0 +1,240 @@ +## マテリアルのリストを保持/操作を提供するクラス。 +class_name MaterialList +extends RefCounted + + +const uuid_util = preload('res://addons/uuid/uuid.gd') + +## マテリアルのリストの要素。 +class PaintMaterialListItem: + var id: String + var name: String = "" + var material: PaintMaterial = null + + func _init() -> void: + id = uuid_util.v4() + + +## マテリアルのリスト。 +var list: Array[PaintMaterialListItem] = [] +## 現在選択中のマテリアル。-1で未選択。 +var selected_index: int = -1 + +## 新規レイヤー作成時の塗りのマテリアルid。 +var new_path_layer_fill_material_id: String +## 新規レイヤー作成時の線のマテリアルid。 +var new_path_layer_line_material_id: String +## 新規レイヤー作成時の塗りのマテリアルid。 +var new_fill_layer_material_id: String + +## 新しいマテリアルの名前のregex。 +var _new_material_regex: RegEx + + +func _init() -> void: + _new_material_regex = RegEx.new() + _new_material_regex.compile("^マテリアル (\\d+)$") + + +##新規ドキュメント用に マテリアルを作成する。 +func create_materials_for_new_documet() -> void: + var fill_material := ColorPaintMaterial.new() + var fill_item := PaintMaterialListItem.new() + fill_item.material = fill_material + fill_item.name = "Default Path Fill" + list.append(fill_item) + new_path_layer_fill_material_id = fill_item.id + + var line_material := ColorPaintMaterial.new() + line_material.color = Color.BLACK + var line_item := PaintMaterialListItem.new() + line_item.material = line_material + line_item.name = "Default Path Line" + list.append(line_item) + new_path_layer_line_material_id = line_item.id + + var fill_layer_material := ColorPaintMaterial.new() + fill_layer_material.color = Color.WHITE + var fill_layer_item := PaintMaterialListItem.new() + fill_layer_item.material = fill_layer_material + fill_layer_item.name = "Default File Layer" + list.append(fill_layer_item) + new_fill_layer_material_id = fill_layer_item.id + + Main.emit_update_material_list_tab() + Main.emit_update_layer_list_tab() + Main.emit_end_edit_gradient() + + +## ドキュメントを閉じる際にマテリアルを削除する。 +func clear() -> void: + list.clear() + selected_index = -1 + new_path_layer_fill_material_id = "" + new_path_layer_line_material_id = "" + new_fill_layer_material_id = "" + + +## 新しいマテリアルの名前を取得する。 +func _get_new_material_name() -> String: + var nums: Array[int] = [] + for item in list: + var result := _new_material_regex.search(item.name) + if result: + nums.append(int(result.get_string(1))) + nums.sort() + + var num := -1 + for i in nums: + if num + 1 == i: + num += 1 + elif num == i: + continue + else: + break + num += 1 + + return tr("MATERIAL") + " " + str(num) + + +## 塗りつぶしマテリアルを追加する。 +func add_color_material() -> void: + var material := ColorPaintMaterial.new() + var item := PaintMaterialListItem.new() + item.material = material + item.name = _get_new_material_name() + if selected_index == -1: + list.insert(0, item) + else: + list.insert(selected_index, item) + Main.emit_update_material_list_tab() + Main.emit_update_layer_list_tab() + Main.emit_end_edit_gradient() + + +## 線形グラデーションマテリアルを追加する。 +func add_linear_gradient_material() -> void: + var material := LinearGradientPaintMaterial.new() + var item := PaintMaterialListItem.new() + item.material = material + item.name = _get_new_material_name() + if selected_index == -1: + list.insert(0, item) + else: + list.insert(selected_index, item) + Main.emit_update_material_list_tab() + Main.emit_update_layer_list_tab() + Main.emit_end_edit_gradient() + + +## 放射グラデーションマテリアルを追加する。 +func add_radial_gradient_material() -> void: + var material := RadialGradientPaintMaterial.new() + var item := PaintMaterialListItem.new() + item.material = material + item.name = _get_new_material_name() + if selected_index == -1: + list.insert(0, item) + else: + list.insert(selected_index, item) + Main.emit_update_material_list_tab() + Main.emit_update_layer_list_tab() + Main.emit_end_edit_gradient() + + +## マテリアルの削除。 +func delete_material() -> void: + if list.size() > 1: + # 削除するidが新規作成用idだったら新規作成用idをリストの先頭のアイテムにする。 + if list[selected_index].id == new_path_layer_fill_material_id: + new_path_layer_fill_material_id = list[0].id + if list[selected_index].id == new_path_layer_line_material_id: + new_path_layer_line_material_id = list[0].id + if list[selected_index].id == new_fill_layer_material_id: + new_fill_layer_material_id = list[0].id + + list.remove_at(selected_index) + if selected_index > 0: + selected_index -= 1 + + Main.emit_update_layer_list_tab() + Main.emit_update_material_list_tab() + Main.emit_end_edit_gradient() + Main.compositor.composite(Main.layers.root, true) + + +## マテリアルのリネーム。 +func rename_material(name: String) -> void: + if not list[selected_index].name == name: + list[selected_index].name = name + Main.emit_update_layer_list_tab() + + +## マテリアルの取得。 +func get_material(id: String) -> PaintMaterial: + for item in list: + if item.id == id: + return item.material + return null + + +## マテリアルの名前取得。 +func get_material_name(id: String) -> String: + for item in list: + if item.id == id: + return item.name + return "=== Not Found ===" + + +## 選択マテリアルを変更する。 +func set_selected_index(index: int) -> void: + selected_index = index + Main.emit_update_material_list_tab() + Main.emit_end_edit_gradient() + + +## マテリアルの並べ替え。 +func reorder_selected_material(index: int) -> void: + var m := list[selected_index] + list.remove_at(selected_index) + if selected_index < index: + index -= 1 + list.insert(index, m) + Main.emit_update_material_list_tab() + Main.emit_end_edit_gradient() + + Main.commit_history() + + +## マテリアルの選択を解除する。 +func clear_selected_index() -> void: + selected_index = -1 + Main.emit_update_material_list_tab() + Main.emit_end_edit_gradient() + + +## コピーしてあるマテリアルをペーストする。 +func paste_copied_material() -> void: + var copied_material := Main.copy_manager.get_copied_material() + if copied_material != null: + if selected_index == -1: + list.insert(0, copied_material) + else: + list.insert(selected_index, copied_material) + + Main.emit_update_material_list_tab() + Main.emit_end_edit_gradient() + + Main.commit_history() + + +## ドキュメントサイズの変更を適用する。 +func change_document_size(new_document_size: Vector2, anchor: PaintLayer.ScaleAnchor) -> void: + for item in list: + if item.material is LinearGradientPaintMaterial: + var material := item.material as LinearGradientPaintMaterial + material.change_document_size(new_document_size, anchor) + elif item.material is RadialGradientPaintMaterial: + var material := item.material as RadialGradientPaintMaterial + material.change_document_size(new_document_size, anchor) + Main.emit_update_layer_list_tab() diff --git a/godot/src/main/paint_layer_list.gd b/godot/src/main/paint_layer_list.gd new file mode 100644 index 0000000..bcab5d2 --- /dev/null +++ b/godot/src/main/paint_layer_list.gd @@ -0,0 +1,1388 @@ +## レイヤーのリストを保持/操作を提供するクラス。 +class_name PaintLayerList +extends RefCounted + + +## documentのレイヤーのルート。 +var root: Array[PaintLayer] = [] + +## 現在選択されているレイヤーの配列。 +var selected_layers: Array[PaintLayer] = [] +## 現在選択されているパスの配列。 +var selected_paths: Array[Path] = [] + +## 最後に選択したPaintLayerの参照。 +## 新しいレイヤーはこのレイヤーの後ろに追加される。 +## 選択中のレイヤーが削除された場合などにnullになる。 +var last_selected_layer: PaintLayer = null +## 最後に選択したPathの参照。 +## 選択中のパスが削除された場合などにnullになる。 +var last_selected_path: Path = null +## 最後に選択したPaintLayerもしくはPathの参照。 +var last_selected_item: Variant = null + +## 現在描画先になっているPathPaintLayerの参照。 +## 新しいパスはこのレイヤーに追加される。 +## PathPaintLayerが存在しない場合などにnullになる。 +var drawing_path_layer: PathPaintLayer = null + +## ドラッグ中のレイヤーのルートの配列。 +## ドラッグ中以外は空の配列となる。 +var dragging_root_layers: Array[PaintLayer] = [] +## ドラッグ中のレイヤーの配列。 +## ドラッグ中以外は空の配列となる。 +var dragging_layers: Array[PaintLayer] = [] +## ドラッグ中のパスの配列。 +## ドラッグ中以外は空の配列となる。 +var dragging_paths: Array[Path] = [] + + +## 新しくドキュメントを作る。 +func new_document(new_document_size: Vector2i) -> void: + Main.document_size = new_document_size + root.append(Main.layer_pool.get_fill_layer()) + root.append(Main.layer_pool.get_path_layer()) + selected_layers = [] + selected_paths = [] + + var path_layer := root[1] as PathPaintLayer + last_selected_layer = path_layer as PaintLayer + last_selected_item = path_layer + drawing_path_layer = path_layer + + Main.compositor.update() + Main.emit_selected_paths_changed() + Main.emit_update_layer_list_tab() + + +## ドキュメントを閉じる。 +func clear() -> void: + for layer in root: + Main.layer_pool.free_layer(layer) + root.clear() + selected_layers.clear() + selected_paths.clear() + last_selected_layer = null + last_selected_path = null + last_selected_item = null + drawing_path_layer = null + dragging_root_layers.clear() + dragging_layers.clear() + dragging_paths.clear() + + +## 新しいレイヤーの名前を取得する。 +func get_new_layer_name() -> String: + var new_layer_regex := RegEx.new() + new_layer_regex.compile("^" + tr("LAYER") + " (\\d+)$") + var nums: Array[int] = [] + for layer in get_flatten_layers(): + var result := new_layer_regex.search(layer.layer_name) + if result: + nums.append(int(result.get_string(1))) + nums.sort() + + var num := -1 + for i in nums: + if num + 1 == i: + num += 1 + elif num == i: + continue + else: + break + num += 1 + + return tr("LAYER") + " " + str(num) + + +## 新しいパスの名前を取得する。 +func get_new_path_name() -> String: + var new_path_regex := RegEx.new() + new_path_regex.compile("^" + tr("PATH") + " (\\d+)$") + var nums: Array[int] = [] + for layer in get_flatten_layers(): + if layer is PathPaintLayer: + var path_layer := layer as PathPaintLayer + for path in path_layer.paths: + var result := new_path_regex.search(path.path_name) + if result: + nums.append(int(result.get_string(1))) + nums.sort() + + var num := -1 + for i in nums: + if num + 1 == i: + num += 1 + elif num == i: + continue + else: + break + num += 1 + + return tr("PATH") + " " + str(num) + + +## 現在選択中のPaintLayerもしくは現在選択中のパスを含むPathPaintLayerのリストを取得する。 +func _get_selected_path_layers() -> Array[PaintLayer]: + var ret: Array[PaintLayer] = [] + + for selected_layer in selected_layers: + ret.append(selected_layer) + + for path in selected_paths: + @warning_ignore("unsafe_cast") + var parent := path.parent_path_layer.get_ref() as PathPaintLayer + if not parent in ret: + ret.append(parent) + + return ret + + +## グループレイヤーの中の選択中のレイヤーの後ろにレイヤーを挿入する。 +func _insert_layer_to_group(group: GroupPaintLayer, layer: PaintLayer) -> bool: + for index in group.child_layers.size(): + var l := group.child_layers[index] + if l == last_selected_layer: + group.child_layers.insert(index + 1, layer) + return true + if l is GroupPaintLayer: + var gl := l as GroupPaintLayer + if _insert_layer_to_group(gl, layer): + return true + return false + + +## 新しいパスレイヤーを最後に選択したレイヤーの後ろに挿入する。 +func add_path_layer() -> PathPaintLayer: + var layer := Main.layer_pool.get_path_layer() + + var inserted := false + + # last_selected_layerがGroupPaintLayerで折りたたまれていなかったらその末尾に追加する。 + if last_selected_layer is GroupPaintLayer: + var gl := last_selected_layer as GroupPaintLayer + if not gl.collapsed: + gl.child_layers.append(layer) + inserted = true + + if not inserted: + # rootからたどってlast_selected_layerを見つけたら直後に追加する + for index in root.size(): + var l := root[index] + if l == last_selected_layer: + root.insert(index + 1, layer) + inserted = true + break + if l is GroupPaintLayer: + var gl := l as GroupPaintLayer + if _insert_layer_to_group(gl, layer): + inserted = true + break + + # last_selected_layerを見つけられなかった場合、先頭に追加する + if not inserted: + root.append(layer) + + update_parent_visible() + set_selected_layer(layer) + + Main.compositor.update() + + return layer + + +## 新しい塗りつぶしレイヤーを最後に選択したレイヤーの後ろに挿入する。 +func add_fill_layer() -> FillPaintLayer: + var layer := Main.layer_pool.get_fill_layer() + + var inserted := false + + # last_selected_layerがGroupPaintLayerで折りたたまれていなかったらその末尾に追加する。 + if last_selected_layer is GroupPaintLayer: + var gl := last_selected_layer as GroupPaintLayer + if not gl.collapsed: + gl.child_layers.append(layer) + inserted = true + + # rootからたどってlast_selected_layerを見つけたら直後に追加する + if not inserted: + for index in root.size(): + var l := root[index] + if l == last_selected_layer: + root.insert(index + 1, layer) + inserted = true + break + if l is GroupPaintLayer: + var gl := l as GroupPaintLayer + if _insert_layer_to_group(gl, layer): + inserted = true + break + + # last_selected_layerを見つけられなかった場合、先頭に追加する + if not inserted: + root.append(layer) + + update_parent_visible() + set_selected_layer(layer) + + Main.compositor.update() + + return layer + + +## 新しいグループレイヤーを最後に選択したレイヤーの後ろに挿入する。 +func add_group_layer() -> GroupPaintLayer: + var layer := Main.layer_pool.get_group_layer() + + var inserted := false + + # last_selected_layerがGroupPaintLayerで折りたたまれていなかったらその末尾に追加する。 + if last_selected_layer is GroupPaintLayer: + var gl := last_selected_layer as GroupPaintLayer + if not gl.collapsed: + gl.child_layers.append(layer) + inserted = true + + # rootからたどってlast_selected_layerを見つけたら直後に追加する + if not inserted: + for index in root.size(): + var l := root[index] + if l == last_selected_layer: + root.insert(index + 1, layer) + inserted = true + break + if l is GroupPaintLayer: + var gl := l as GroupPaintLayer + if _insert_layer_to_group(gl, layer): + inserted = true + break + + # last_selected_layerを見つけられなかった場合、先頭に追加する + if not inserted: + root.append(layer) + + update_parent_visible() + set_selected_layer(layer) + + Main.compositor.update() + + return layer + + +## グループレイヤー内の選択中のレイヤー/パスを削除する。 +func _delete_selected_layer_path_in_group(group: GroupPaintLayer) -> void: + var layer_index: int = 0 + while layer_index < group.child_layers.size(): + var layer := group.child_layers[layer_index] + # 選択中のパスがPathPaintLayerに含まれていたらそのPathを削除する + if layer is PathPaintLayer: + var path_layer := layer as PathPaintLayer + var path_deleted := false + for path in selected_paths: + var index := 0 + while index < path_layer.paths.size(): + if path_layer.paths[index] == path: + path_layer.paths.remove_at(index) + path_deleted = true + else: + index += 1 + if path_deleted: + path_layer.set_need_composite() + + # グループレイヤーだったら再帰的に中身を削除する + if layer is GroupPaintLayer: + var group_layer := layer as GroupPaintLayer + _delete_selected_layer_path_in_group(group_layer) + + # レイヤーが選択中レイヤーに含まれていたらレイヤーを削除する + var layer_deleted := false + for index in selected_layers.size(): + var l := selected_layers[index] + if l == layer: + selected_layers.remove_at(index) + Main.layer_pool.free_layer(layer) + + for i in group.child_layers.size(): + if layer == group.child_layers[i]: + group.child_layers.remove_at(i) + layer_deleted = true + break + break + + if layer_deleted and layer is PathPaintLayer: + var path_layer := layer as PathPaintLayer + if path_layer == drawing_path_layer: + drawing_path_layer = null + + if layer_deleted: + group.need_composite = true + else: + layer_index += 1 + + +## 選択中のレイヤー/パスを削除する。 +func delete_selected_layer_path() -> void: + # selected_layersをrootからたどって削除する + var layer_index: int = 0 + while layer_index < root.size(): + var layer := root[layer_index] + + # 選択中のパスがPathPaintLayerに含まれていたらそのPathを削除する + if layer is PathPaintLayer: + var path_layer := layer as PathPaintLayer + var path_deleted := false + for path in selected_paths: + var i := 0 + while i < path_layer.paths.size(): + if path_layer.paths[i] == path: + path_layer.paths.remove_at(i) + path_deleted = true + else: + i += 1 + if path_deleted: + path_layer.set_need_composite() + + # グループレイヤーだったら再帰的に中身を削除する + if layer is GroupPaintLayer: + var group_layer := layer as GroupPaintLayer + _delete_selected_layer_path_in_group(group_layer) + + # レイヤーが選択中レイヤーに含まれていたらレイヤーを削除する + var layer_deleted := false + for index in selected_layers.size(): + var l := selected_layers[index] + if l == layer: + selected_layers.remove_at(index) + Main.layer_pool.free_layer(layer) + + for i in root.size(): + if layer == root[i]: + root.remove_at(i) + layer_deleted = true + break + break + + if layer_deleted and layer is PathPaintLayer: + var path_layer := layer as PathPaintLayer + if path_layer == drawing_path_layer: + drawing_path_layer = null + + if layer_deleted: + Main.compositor.need_composite = true + else: + layer_index += 1 + + Main.compositor.update() + + # last_selected_layerやdrawing_path_layerをnullにする + last_selected_layer = null + last_selected_path = null + last_selected_item = null + # selected_layers/selected_pathsをクリアする + selected_layers.clear() + selected_paths.clear() + # シグナルを発火する + Main.emit_selected_paths_changed() + Main.emit_update_layer_list_tab() + + +## 子孫要素の選択状況を変える。 +func _select_change_children(layer: PaintLayer, select: bool) -> void: + if layer is PathPaintLayer: + var path_layer := layer as PathPaintLayer + for path in path_layer.paths: + if select: + if not path in selected_paths: + selected_paths.append(path) + else: + var index := 0 + while index < selected_paths.size(): + var selected_path := selected_paths[index] + if selected_path == path: + selected_paths.remove_at(index) + + if layer is GroupPaintLayer: + var group_layer := layer as GroupPaintLayer + for child_layer in group_layer.child_layers: + if select: + if not child_layer in selected_layers: + selected_layers.append(child_layer) + else: + var index := 0 + while index < selected_layers.size(): + var selected_layer := selected_layers[index] + if selected_layer == layer: + selected_layers.remove_at(index) + + _select_change_children(child_layer, select) + + +## 折りたたみなども見つつ選択状況を変更する。 +func _set_selection_of_layer(layer: PaintLayer, select: bool) -> void: + if select: + if not layer in selected_layers: + selected_layers.append(layer) + else: + var index := 0 + while index < selected_layers.size(): + var selected_layer := selected_layers[index] + if selected_layer == layer: + selected_layers.remove_at(index) + + if layer is PathPaintLayer: + var path_layer := layer as PathPaintLayer + if path_layer.collapsed: + _select_change_children(layer, select) + if layer is GroupPaintLayer: + var group_layer := layer as GroupPaintLayer + if group_layer.collapsed: + _select_change_children(layer, select) + + +## 選択中のレイヤーを変更する。 +## そのレイヤーがPathPaintLayerの場合、drawing_path_layerも更新する。 +func set_selected_layer(layer: PaintLayer) -> void: + selected_layers.clear() + selected_paths.clear() + _set_selection_of_layer(layer, true) + last_selected_layer = layer + last_selected_item = layer + if layer is PathPaintLayer: + drawing_path_layer = layer as PathPaintLayer + + Main.materials.clear_selected_index() + Main.emit_selected_paths_changed() + Main.emit_update_layer_list_tab() + + +## レイヤーの選択状態を更新する。 +## そのレイヤーがPathPaintLayerの場合、drawing_path_layerも更新する。 +func add_or_remove_selected_layer(layer: PaintLayer) -> void: + if layer in selected_layers: + _set_selection_of_layer(layer, false) + Main.emit_selected_paths_changed() + Main.emit_update_layer_list_tab() + else: + _set_selection_of_layer(layer, true) + last_selected_layer = layer + last_selected_item = layer + if layer is PathPaintLayer: + drawing_path_layer = layer as PathPaintLayer + + Main.materials.clear_selected_index() + Main.emit_selected_paths_changed() + Main.emit_update_layer_list_tab() + + +## last_selected_itmeからfinish_layerまでの範囲を選択する。 +## 順に辿っていってlast_selected_itemを見つけたらflagをtrueに、finish_layerにたどり着いたらflagをfalseにする。 +func _add_selected_items_layer_range_in_group(group_layer: GroupPaintLayer, finish_layer: PaintLayer, flag: bool) -> bool: + for index in group_layer.child_layers.size(): + var layer := group_layer.child_layers[group_layer.child_layers.size() - index - 1] + var prev_flag := flag + if layer == last_selected_item: + flag = not flag + if layer == finish_layer: + flag = not flag + if prev_flag or flag: + _set_selection_of_layer(layer, true) + if layer is PathPaintLayer: + var pl := layer as PathPaintLayer + for i in pl.paths.size(): + var path := pl.paths[pl.paths.size() - i - 1] + prev_flag = flag + if path == last_selected_item: + flag = not flag + if prev_flag or flag: + if not path in selected_paths: + selected_paths.append(path) + if layer is GroupPaintLayer: + var gl := layer as GroupPaintLayer + flag = _add_selected_items_layer_range_in_group(gl, finish_layer, flag) + return flag + + +## last_selected_itmeからfinish_layerまでの範囲を選択する。 +## 順に辿っていってlast_selected_itemを見つけたらflagをtrueに、finish_layerにたどり着いたらflagをfalseにする。 +func _add_selected_items_layer_range(finish_layer: PaintLayer) -> void: + var flag := false + for index in root.size(): + var layer := root[root.size() - index - 1] + var prev_flag := flag + if layer == last_selected_item: + flag = not flag + if layer == finish_layer: + flag = not flag + if prev_flag or flag: + _set_selection_of_layer(layer, true) + if layer is PathPaintLayer: + var pl := layer as PathPaintLayer + for i in pl.paths.size(): + var path := pl.paths[pl.paths.size() - i - 1] + prev_flag = flag + if path == last_selected_item: + flag = not flag + if prev_flag or flag: + if not path in selected_paths: + selected_paths.append(path) + if layer is GroupPaintLayer: + var gl := layer as GroupPaintLayer + flag = _add_selected_items_layer_range_in_group(gl, finish_layer, flag) + + +## 最後に選択したアイテムから引数のlayerまでの範囲を選択する。 +## 必要に応じてdrawing_path_layerも更新する。 +func set_selected_layer_range(layer: PaintLayer) -> void: + if last_selected_item == null: + set_selected_layer(layer) + return + if last_selected_item == layer: + return + selected_layers.clear() + selected_paths.clear() + _add_selected_items_layer_range(layer) + last_selected_item = layer + last_selected_layer = layer + if last_selected_layer is PathPaintLayer: + drawing_path_layer = last_selected_layer as PathPaintLayer + + Main.materials.clear_selected_index() + Main.emit_selected_paths_changed() + Main.emit_update_layer_list_tab() + + +## 最後に選択したアイテムから引数のlayerまでの範囲を追加で選択する。 +## 必要に応じてdrawing_path_layerも更新する。 +func add_selected_layer_range(layer: PaintLayer) -> void: + if last_selected_item == null: + set_selected_layer(layer) + return + if last_selected_item == layer: + return + _add_selected_items_layer_range(layer) + last_selected_item = layer + last_selected_layer = layer + if last_selected_layer is PathPaintLayer: + drawing_path_layer = last_selected_layer as PathPaintLayer + + Main.materials.clear_selected_index() + Main.emit_selected_paths_changed() + Main.emit_update_layer_list_tab() + + +## 選択中のパスを変更する。 +## その親のレイヤーをdrawing_path_layerとlast_selected_layerに変更する。 +func set_selected_path(path: Path) -> void: + selected_layers.clear() + selected_paths.clear() + selected_paths.append(path) + last_selected_path = path + last_selected_item = path + + @warning_ignore("unsafe_cast") + var parent := path.parent_path_layer.get_ref() as PathPaintLayer + if parent: + drawing_path_layer = parent + last_selected_layer = parent + + Main.materials.clear_selected_index() + Main.emit_selected_paths_changed() + Main.emit_update_layer_list_tab() + + +## パスの選択状態を変更する。 +## その親のレイヤーをdrawing_path_layerとlast_selected_layerに変更する。 +func add_or_remove_selected_path(path: Path) -> void: + if path in selected_paths: + var index := 0 + while index < selected_paths.size(): + var selected_path := selected_paths[index] + if selected_path == path: + selected_paths.remove_at(index) + Main.emit_selected_paths_changed() + Main.emit_update_layer_list_tab() + return + else: + index += 1 + selected_paths.append(path) + last_selected_path = path + last_selected_item = path + + @warning_ignore("unsafe_cast") + var parent := path.parent_path_layer.get_ref() as PathPaintLayer + if parent: + drawing_path_layer = parent + last_selected_layer = parent + + Main.materials.clear_selected_index() + Main.emit_selected_paths_changed() + Main.emit_update_layer_list_tab() + + +## last_selected_itmeからfinish_pathまでの範囲を選択する。 +## 順に辿っていってlast_selected_itemを見つけたらflagをtrueに、finish_layerにたどり着いたらflagをfalseにする。 +func _add_selected_items_path_range_in_group(group_layer: GroupPaintLayer, finish_path: Path, flag: bool) -> bool: + for index in group_layer.child_layers.size(): + var layer := group_layer.child_layers[group_layer.child_layers.size() - index - 1] + var prev_flag := flag + if layer == last_selected_item: + flag = not flag + if prev_flag or flag: + if not layer in selected_layers: + selected_layers.append(layer) + if layer is PathPaintLayer: + var pl := layer as PathPaintLayer + for i in pl.paths.size(): + var path := pl.paths[pl.paths.size() - i - 1] + prev_flag = flag + if path == last_selected_item: + flag = not flag + if path == finish_path: + flag = not flag + if prev_flag or flag: + if not path in selected_paths: + selected_paths.append(path) + if layer is GroupPaintLayer: + var gl := layer as GroupPaintLayer + flag = _add_selected_items_path_range_in_group(gl, finish_path, flag) + return flag + + +## last_selected_itmeからfinish_layerまでの範囲を選択する。 +## 順に辿っていってlast_selected_itemを見つけたらflagをtrueに、finish_layerにたどり着いたらflagをfalseにする。 +func _add_selected_items_path_range(finish_path: Path) -> void: + var flag := false + for index in root.size(): + var layer := root[root.size() - index - 1] + var prev_flag := flag + if layer == last_selected_item: + flag = not flag + if prev_flag or flag: + if not layer in selected_layers: + selected_layers.append(layer) + if layer is PathPaintLayer: + var pl := layer as PathPaintLayer + for i in pl.paths.size(): + var path := pl.paths[pl.paths.size() - i - 1] + prev_flag = flag + if path == last_selected_item: + flag = not flag + if path == finish_path: + flag = not flag + if prev_flag or flag: + if not path in selected_paths: + selected_paths.append(path) + if layer is GroupPaintLayer: + var gl := layer as GroupPaintLayer + flag = _add_selected_items_path_range_in_group(gl, finish_path, flag) + + +## 最後に選択したアイテムから引数のpathまでの範囲を選択する。 +## 必要に応じてdrawing_path_layerも更新する。 +func set_selected_path_range(path: Path) -> void: + if last_selected_item == null: + set_selected_path(path) + return + if last_selected_item == path: + return + selected_layers.clear() + selected_paths.clear() + _add_selected_items_path_range(path) + last_selected_item = path + last_selected_path = path + + @warning_ignore("unsafe_cast") + var parent := path.parent_path_layer.get_ref() as PathPaintLayer + if parent: + drawing_path_layer = parent + last_selected_layer = parent + + Main.materials.clear_selected_index() + Main.emit_selected_paths_changed() + Main.emit_update_layer_list_tab() + + +## 最後に選択したアイテムから引数のpathまでの範囲を追加で選択する。 +## 必要に応じてdrawing_path_layerも更新する。 +func add_selected_path_range(path: Path) -> void: + if last_selected_item == null: + set_selected_path(path) + return + if last_selected_item == path: + return + _add_selected_items_path_range(path) + last_selected_item = path + last_selected_path = path + + @warning_ignore("unsafe_cast") + var parent := path.parent_path_layer.get_ref() as PathPaintLayer + if parent: + drawing_path_layer = parent + last_selected_layer = parent + + Main.materials.clear_selected_index() + Main.emit_selected_paths_changed() + Main.emit_update_layer_list_tab() + + +## 選択中のレイヤー/パスをクリアする。 +func clear_selected_items() -> void: + selected_layers.clear() + selected_paths.clear() + Main.emit_selected_paths_changed() + Main.emit_update_layer_list_tab() + + +## 引数のGroupPaintLayerの中のすべてのレイヤーをdragging_layersに追加する。 +func _add_group_layer_to_dragging(group_layer: GroupPaintLayer) -> void: + for layer in group_layer.child_layers: + dragging_layers.append(layer) + if layer is PathPaintLayer: + var path_layer := layer as PathPaintLayer + for path in path_layer.paths: + dragging_paths.append(path) + if layer is GroupPaintLayer: + var gl := layer as GroupPaintLayer + _drag_start_layer_group(gl) + + +## 引数のGroupPaintLayerの中のドラッグ中のレイヤーをdragging_layersに追加する。 +func _drag_start_layer_group(group_layer: GroupPaintLayer) -> void: + for layer in group_layer.child_layers: + var layer_selected := layer in selected_layers + if layer is PathPaintLayer: + var path_layer := layer as PathPaintLayer + for path in path_layer.paths: + if path in selected_paths: + layer_selected = true + if layer_selected: + dragging_layers.append(layer) + if layer is PathPaintLayer: + var path_layer := layer as PathPaintLayer + for path in path_layer.paths: + dragging_paths.append(path) + if layer is GroupPaintLayer: + var gl := layer as GroupPaintLayer + _add_group_layer_to_dragging(gl) + elif layer is GroupPaintLayer: + var gl := layer as GroupPaintLayer + _drag_start_layer_group(gl) + + +## ドラッグ中のPathとLayerを計算し、dragging_pathsとdragging_layersを埋める。 +func drag_start() -> void: + if selected_layers.size() > 0: + for layer in root: + var layer_selected := layer in selected_layers + if layer is PathPaintLayer: + var path_layer := layer as PathPaintLayer + for path in path_layer.paths: + if path in selected_paths: + layer_selected = true + if layer_selected: + dragging_layers.append(layer) + if layer is PathPaintLayer: + var path_layer := layer as PathPaintLayer + for path in path_layer.paths: + dragging_paths.append(path) + if layer is GroupPaintLayer: + var gl := layer as GroupPaintLayer + _add_group_layer_to_dragging(gl) + elif layer is GroupPaintLayer: + var gl := layer as GroupPaintLayer + _drag_start_layer_group(gl) + + # ドラッグ中のレイヤーのうち、ルート側のものを取得 + for layer in dragging_layers: + var has_parent := false + for l in dragging_layers: + if l is GroupPaintLayer: + var gl := l as GroupPaintLayer + for ll in gl.child_layers: + if ll == layer: + has_parent = true + if not has_parent: + dragging_root_layers.append(layer) + + dragging_root_layers.reverse() + else: + dragging_paths = selected_paths.duplicate() + + +## 現在ドラッグ中のパスを引数のGroupPaintLayerの中から除去する。 +func _remove_dragging_paths_from_group_layer(gl: GroupPaintLayer, parent: PathPaintLayer, insert_at: int) -> int: + for layer in gl.child_layers: + if layer is PathPaintLayer: + var path_layer := layer as PathPaintLayer + var index := 0 + var dirty_path_layer := false + while index < path_layer.paths.size(): + var path := path_layer.paths[index] + if path in dragging_paths: + path_layer.paths.remove_at(index) + dirty_path_layer = true + if path_layer == parent and insert_at > index: + insert_at -= 1 + else: + index += 1 + if dirty_path_layer: + path_layer.set_need_composite() + elif layer is GroupPaintLayer: + var group_layer := layer as GroupPaintLayer + insert_at = _remove_dragging_paths_from_group_layer(group_layer, parent, insert_at) + return insert_at + + +## ドラッグ中のパスを特定の位置に移動する。 +func drag_move_path(insert_at: int, parent: PathPaintLayer) -> void: + for layer in root: + if layer is PathPaintLayer: + var path_layer := layer as PathPaintLayer + var index := 0 + var dirty_path_layer := false + while index < path_layer.paths.size(): + var path := path_layer.paths[index] + if path in selected_paths: + path_layer.paths.remove_at(index) + dirty_path_layer = true + if path_layer == parent and insert_at > index: + insert_at -= 1 + else: + index += 1 + if dirty_path_layer: + path_layer.set_need_composite() + elif layer is GroupPaintLayer: + var group_layer := layer as GroupPaintLayer + insert_at = _remove_dragging_paths_from_group_layer(group_layer, parent, insert_at) + for path in dragging_paths: + parent.paths.insert(insert_at, path) + path.parent_path_layer = weakref(parent) + parent.set_need_composite() + update_parent_visible() + update_parent_locked() + + +## 現在ドラッグ中のレイヤーのルート達を引数のGroupPaintLayerの中から除去する。 +func _remove_dragging_root_layers_from_group_layer(gl: GroupPaintLayer, parent: GroupPaintLayer, insert_at: int) -> int: + var index := 0 + while index < gl.child_layers.size(): + var layer := gl.child_layers[index] + if layer in dragging_root_layers: + gl.child_layers.remove_at(index) + gl.need_composite = true + if gl == parent and insert_at > index: + insert_at -= 1 + else: + index += 1 + if layer is GroupPaintLayer: + var group_layer := layer as GroupPaintLayer + insert_at = _remove_dragging_root_layers_from_group_layer(group_layer, parent, insert_at) + return insert_at + + +## ドラッグ中のレイヤーを特定の位置に移動する。 +func drag_move_layer(insert_at: int, parent: GroupPaintLayer) -> void: + var index := 0 + while index < root.size(): + var layer := root[index] + if layer in dragging_root_layers: + root.remove_at(index) + Main.compositor.need_composite = true + if parent == null and insert_at > index: + insert_at -= 1 + else: + index += 1 + if layer is GroupPaintLayer: + var group_layer := layer as GroupPaintLayer + insert_at = _remove_dragging_root_layers_from_group_layer(group_layer, parent, insert_at) + if parent: + for layer in dragging_root_layers: + parent.child_layers.insert(insert_at, layer) + parent.need_composite = true + else: + for layer in dragging_root_layers: + root.insert(insert_at, layer) + Main.compositor.need_composite = true + update_parent_visible() + update_parent_locked() + + +## ドラッグ中の状態を解除する。 +func drag_end() -> void: + dragging_root_layers.clear() + dragging_layers.clear() + dragging_paths.clear() + + +## 新しいPathの追加。 +## drawing_path_layerが存在する場合、そのレイヤーにパスを追加する。 +## そうでない場合、新しいPathPaintLayerを作って追加する。 +func add_path() -> Path: + if not drawing_path_layer: + drawing_path_layer = add_path_layer() + + if drawing_path_layer.is_locked(): + return null + + # 新しいパスを追加 + var new_path: Path = null + if last_selected_path and selected_paths.size() > 0: + # 現在パスが選択中の場合、最後に選択したパスの次に挿入する + new_path = drawing_path_layer.add_insert_path_after_path(last_selected_path) + else: + # 現在パスが選択されていない場合、単純に末尾にパスを追加する + new_path = drawing_path_layer.add_path() + update_parent_visible() + set_selected_path(new_path) + + return new_path + + +## 現在選択中のレイヤーの塗り色を変更する。 +func set_fill_color_to_selected_layer(id: String) -> void: + # マテリアルidが存在しない場合は何もしない + var exists_material := false + for item in Main.materials.list: + if item.id == id: + exists_material = true + if not exists_material: + return + + # 選択中のレイヤーのfillのマテリアルを変更する + for layer in selected_layers: + if layer.is_locked(): + continue + if layer is PathPaintLayer: + var path_layer := layer as PathPaintLayer + path_layer.fill_material_id = id + + # 新規作成時のマテリアルを現在セットしたマテリアルに更新しておく + Main.materials.new_path_layer_fill_material_id = id + + Main.emit_update_layer_list_tab() + + +## 現在選択中のレイヤーの線の色を変更する。 +func set_line_color_to_selected_layer(id: String) -> void: + # マテリアルidが存在しない場合は何もしない + var exists_material := false + for item in Main.materials.list: + if item.id == id: + exists_material = true + if not exists_material: + return + + # 選択中のレイヤーのlineのマテリアルを変更する + for layer in selected_layers: + if layer.is_locked(): + continue + if layer is PathPaintLayer: + var path_layer := layer as PathPaintLayer + path_layer.outline_material_id = id + + # 新規作成時のマテリアルを現在セットしたマテリアルに更新しておく + Main.materials.new_path_layer_line_material_id = id + + Main.emit_update_layer_list_tab() + + +## 現在選択中のレイヤーの塗りのオンオフを設定する。 +func set_fill_of_selected_layers(fill: bool) -> void: + for layer in _get_selected_path_layers(): + if layer.is_locked(): + continue + if layer is PathPaintLayer: + var path_layer := layer as PathPaintLayer + path_layer.filled = fill + path_layer.set_need_composite() + + Main.emit_update_layer_list_tab() + + +## 現在選択中のレイヤーの線のオンオフを設定する。 +func set_line_of_selected_layers(line: bool) -> void: + for layer in _get_selected_path_layers(): + if layer.is_locked(): + continue + if layer is PathPaintLayer: + var path_layer := layer as PathPaintLayer + path_layer.outlined = line + path_layer.set_need_composite() + + Main.emit_update_layer_list_tab() + + +## 現在選択中のパスレイヤーの塗りマテリアルを設定する。 +func set_fill_material_of_selected_path_layers(material_id: String) -> void: + for layer in _get_selected_path_layers(): + if layer.is_locked(): + continue + if layer is PathPaintLayer: + var path_layer := layer as PathPaintLayer + path_layer.fill_material_id = material_id + path_layer.set_need_composite() + + Main.emit_update_layer_list_tab() + + +## 現在選択中のパスレイヤーの線マテリアルを設定する。 +func set_line_material_of_selected_path_layers(material_id: String) -> void: + for layer in _get_selected_path_layers(): + if layer.is_locked(): + continue + if layer is PathPaintLayer: + var path_layer := layer as PathPaintLayer + path_layer.outline_material_id = material_id + path_layer.set_need_composite() + + Main.emit_update_layer_list_tab() + + +## 現在選択中のパスレイヤーの線幅を設定する。 +func set_line_width_of_selected_path_layers(width: float) -> void: + for layer in _get_selected_path_layers(): + if layer.is_locked(): + continue + if layer is PathPaintLayer: + var path_layer := layer as PathPaintLayer + path_layer.outline_width = width + path_layer.update_outline_polygon() + path_layer.set_need_composite() + + Main.emit_update_layer_list_tab() + + +## 現在選択中の塗りつぶしレイヤーの塗りマテリアルを設定する。 +func set_fill_material_of_selected_fill_layers(material_id: String) -> void: + for layer in _get_selected_path_layers(): + if layer.is_locked(): + continue + if layer is FillPaintLayer: + var fill_layer := layer as FillPaintLayer + fill_layer.fill_material_id = material_id + fill_layer.need_composite = true + + Main.emit_update_layer_list_tab() + + +## 現在選択中パスのopen/closedを切り替える。 +func change_closed_of_selected_path() -> void: + var closed := not last_selected_path.closed if last_selected_path else true + for path in selected_paths: + path.change_closed(closed) + + Main.emit_update_layer_list_tab() + + +## 現在選択中パスのbooleanを切り替える。 +func change_boolean_of_selected_path(boolean: Path.Boolean) -> void: + for path in selected_paths: + path.change_boolean(boolean) + + Main.emit_update_layer_list_tab() + + +## 現在選択中のレイヤーのクリッピングを切り替える。 +func change_clipping_of_selected_layer() -> void: + var clipped := not last_selected_layer.clipped if last_selected_layer else true + for layer in _get_selected_path_layers(): + layer.update_clipped(clipped) + + Main.emit_update_layer_list_tab() + + +## 現在選択中のレイヤーのブレンドモードを切り替える。 +func set_blend_mode_of_selected_layer(blend_mode: PaintLayer.BlendMode) -> void: + for layer in _get_selected_path_layers(): + layer.update_blend_mode(blend_mode) + + Main.emit_update_layer_list_tab() + + +## 現在選択中のレイヤーのアルファをセットする。 +func set_alpha_of_selected_layer(alpha: int) -> void: + for layer in _get_selected_path_layers(): + layer.update_alpha(alpha) + + Main.emit_update_layer_list_tab() + + +## 現在選択中のレイヤーのロック状態を切り替える。 +func change_locked_of_selected_item() -> void: + var locked := not all_locked_item_in_selected() + for layer in selected_layers: + layer.update_locked(locked) + for path in selected_paths: + path.update_locked(locked) + update_parent_locked() + + Main.emit_update_layer_list_tab() + Main.emit_selected_paths_changed() + + +## 選択中のレイヤー/パスにロック中のレイヤー/パスが含まれているかを取得する。 +func any_locked_item_in_selected() -> bool: + for layer in selected_layers: + if layer.is_locked(): + return true + for path in selected_paths: + if path.is_locked(): + return true + return false + + +## 選択中のレイヤー/パスがすべてロック中かどうかを取得する。 +func all_locked_item_in_selected() -> bool: + if selected_layers.size() == 0 and selected_paths.size() == 0: + return false + for layer in selected_layers: + if not layer.locked: + return false + for path in selected_paths: + if not path.locked: + return false + return true + + +## ルートから親の表示状態を更新する。 +func update_parent_visible() -> void: + for child_layer in root: + if child_layer is PathPaintLayer: + var path_layer := child_layer as PathPaintLayer + path_layer.update_parent_visible(true) + elif child_layer is FillPaintLayer: + var fill_layer := child_layer as FillPaintLayer + fill_layer.update_parent_visible(true) + elif child_layer is GroupPaintLayer: + var group_layer := child_layer as GroupPaintLayer + group_layer.update_parent_visible(true) + + +## ルートから親のロック状態を更新する。 +func update_parent_locked() -> void: + for child_layer in root: + if child_layer is PathPaintLayer: + var path_layer := child_layer as PathPaintLayer + path_layer.update_parent_locked(false) + elif child_layer is FillPaintLayer: + var fill_layer := child_layer as FillPaintLayer + fill_layer.update_parent_locked(false) + elif child_layer is GroupPaintLayer: + var group_layer := child_layer as GroupPaintLayer + group_layer.update_parent_locked(false) + Main.emit_update_layer_list_tab() + + +## グループレイヤーの中の選択中のレイヤーの後ろにレイヤーの配列を挿入する。 +func _insert_layers_to_group(group: GroupPaintLayer, layers: Array[PaintLayer]) -> bool: + for index in group.child_layers.size(): + var l := group.child_layers[index] + if l == last_selected_layer: + for layer in layers: + group.child_layers.insert(index + 1, layer) + return true + if l is GroupPaintLayer: + var gl := l as GroupPaintLayer + if _insert_layers_to_group(gl, layers): + return true + return false + + +## グループレイヤーの中に選択中のパスを含む場合にcollapseを開く。 +func _open_collapsed_in_parent_in_group(group_layer: GroupPaintLayer, path_layer: PathPaintLayer) -> bool: + for layer in group_layer.child_layers: + if layer is PathPaintLayer: + var pl := layer as PathPaintLayer + if path_layer == pl: + path_layer.collapsed = false + return true + if layer is GroupPaintLayer: + var gl := layer as GroupPaintLayer + if _open_collapsed_in_parent_in_group(gl, path_layer): + gl.collapsed = false + return true + return false + + +## PathPaintLayerの親をたどってすべての先祖のレイヤーのcollapseを開く。 +func _open_collapsed_in_parent(path_layer: PathPaintLayer) -> void: + for layer in root: + if layer is PathPaintLayer: + var pl := layer as PathPaintLayer + if path_layer == pl: + path_layer.collapsed = false + return + if layer is GroupPaintLayer: + var gl := layer as GroupPaintLayer + if _open_collapsed_in_parent_in_group(gl, path_layer): + gl.collapsed = false + return + + +## コピーされたレイヤー/パスをペーストする。 +func paste_copied_layers() -> void: + var copied_layers := Main.copy_manager.get_copied_layers() + var copied_paths := Main.copy_manager.get_copied_paths() + if copied_layers.size() != 0: + var inserted := false + + # last_selected_layerがGroupPaintLayerで折りたたまれていなかったらその末尾に追加する。 + if last_selected_layer is GroupPaintLayer: + var gl := last_selected_layer as GroupPaintLayer + if not gl.collapsed: + gl.child_layers.append_array(copied_layers) + inserted = true + + # rootからたどってlast_selected_layerを見つけたら直後に追加する + if not inserted: + for index in root.size(): + var l := root[index] + if l == last_selected_layer: + for layer in copied_layers: + root.insert(index + 1, layer) + inserted = true + break + if l is GroupPaintLayer: + var gl := l as GroupPaintLayer + if _insert_layers_to_group(gl, copied_layers): + inserted = true + break + + # last_selected_layerを見つけられなかった場合、先頭に追加する + if not inserted: + root.append_array(copied_layers) + + for layer in copied_layers: + layer.need_composite = true + if layer is PathPaintLayer: + var path_layer := layer as PathPaintLayer + path_layer.update_child_paths_thumbnail() + + selected_layers.clear() + selected_paths.clear() + for layer in copied_layers: + add_or_remove_selected_layer(layer) + if layer is PathPaintLayer: + var path_layer := layer as PathPaintLayer + path_layer.update_child_paths_thumbnail() + + update_parent_visible() + + Main.compositor.update() + + Main.emit_selected_paths_changed() + Main.emit_update_layer_list_tab() + + Main.commit_history() + + elif copied_paths.size() != 0: + if drawing_path_layer == null: + drawing_path_layer = add_path_layer() + copied_paths.reverse() + drawing_path_layer.paths.append_array(copied_paths) + for path in copied_paths: + path.parent_path_layer = weakref(drawing_path_layer) + drawing_path_layer.set_need_composite() + + selected_layers.clear() + selected_paths.clear() + for path in copied_paths: + add_or_remove_selected_path(path) + drawing_path_layer.update_child_paths_thumbnail() + + _open_collapsed_in_parent(drawing_path_layer) + + update_parent_visible() + Main.emit_selected_paths_changed() + Main.emit_update_layer_list_tab() + + Main.commit_history() + + +## idからグループレイヤー内のレイヤーを取得する。 +func _get_layer_by_id_in_group(group: GroupPaintLayer, id: String) -> PaintLayer: + for layer in group.child_layers: + if layer.id == id: + return layer + elif layer is GroupPaintLayer: + var gl := layer as GroupPaintLayer + var l := _get_layer_by_id_in_group(gl, id) + if l: + return l + return null + + +## idからレイヤーを取得する。 +func get_layer_by_id(id: String) -> PaintLayer: + for layer in root: + if layer.id == id: + return layer + elif layer is GroupPaintLayer: + var gl := layer as GroupPaintLayer + var l := _get_layer_by_id_in_group(gl, id) + if l: + return l + return null + + +## idからグループレイヤー内のパスを取得する。 +func _get_path_by_id_in_group(group: GroupPaintLayer, id: String) -> Path: + for layer in group.child_layers: + if layer is PathPaintLayer: + var path_layer := layer as PathPaintLayer + for path in path_layer.paths: + if path.id == id: + return path + elif layer is GroupPaintLayer: + var gl := layer as GroupPaintLayer + var p := _get_path_by_id_in_group(gl, id) + if p: + return p + return null + + +## idからパスを取得する。 +func get_path_by_id(id: String) -> Path: + for layer in root: + if layer is PathPaintLayer: + var path_layer := layer as PathPaintLayer + for path in path_layer.paths: + if path.id == id: + return path + elif layer is GroupPaintLayer: + var gl := layer as GroupPaintLayer + var p := _get_path_by_id_in_group(gl, id) + if p: + return p + return null + + +## ドキュメントサイズの変更を適用する。 +func change_document_size(new_document_size: Vector2, _anchor: PaintLayer.ScaleAnchor) -> void: + for layer in root: + if layer is PathPaintLayer: + var path_layer := layer as PathPaintLayer + path_layer.change_document_size(new_document_size, _anchor) + elif layer is GroupPaintLayer: + var group_layer := layer as GroupPaintLayer + group_layer.change_document_size(new_document_size, _anchor) + + +## グループレイヤーをたどってflatten_layersに追加する。 +func _add_flatten_layers_in_group(group: GroupPaintLayer, flatten_layers: Array[PaintLayer]) -> void: + for layer in group.child_layers: + flatten_layers.append(layer) + if layer is GroupPaintLayer: + var gl := layer as GroupPaintLayer + _add_flatten_layers_in_group(gl, flatten_layers) + + +## flattenしたレイヤーを取得する。 +func get_flatten_layers() -> Array[PaintLayer]: + var flatten_layers: Array[PaintLayer] = [] + for layer in root: + flatten_layers.append(layer) + if layer is GroupPaintLayer: + var group_layer := layer as GroupPaintLayer + _add_flatten_layers_in_group(group_layer, flatten_layers) + return flatten_layers diff --git a/godot/src/material/color_paint_material.gd b/godot/src/material/color_paint_material.gd new file mode 100644 index 0000000..3002250 --- /dev/null +++ b/godot/src/material/color_paint_material.gd @@ -0,0 +1,7 @@ +## 単色塗りつぶしのマテリアル。 +class_name ColorPaintMaterial +extends PaintMaterial + + +## マテリアルの色。 +var color: Color = Color.AQUAMARINE diff --git a/godot/src/material/linear_gradient_paint_material.gd b/godot/src/material/linear_gradient_paint_material.gd new file mode 100644 index 0000000..5101bb7 --- /dev/null +++ b/godot/src/material/linear_gradient_paint_material.gd @@ -0,0 +1,60 @@ +## 線形グラデーションのマテリアル。 +class_name LinearGradientPaintMaterial +extends PaintMaterial + + +## グラデーション。 +var gradient: Gradient = Gradient.new() +## グラデーションのテクスチャ。 +var gradient_texture: GradientTexture1D = GradientTexture1D.new() + +## グラデーションの始点。 +var start_point: Vector2 = Vector2(0, 0) +## グラデーションの終点。 +var end_point: Vector2 = Vector2(1, 0) + + +func _init() -> void: + gradient_texture.set_gradient(gradient) + gradient_texture.set_width(512) + + gradient.colors = PackedColorArray([Color(1, 1, 1, 1), Color(1, 0, 0, 1), Color(0, 0, 0, 1)]) + gradient.offsets = PackedFloat32Array([0, 0.5, 1]) + + var quater_size := Main.document_size / 4 + start_point = quater_size + end_point = quater_size * 3 + + +## ドキュメントサイズの変更を適用する。 +func change_document_size(new_document_size: Vector2, anchor: PaintLayer.ScaleAnchor) -> void: + var prev_document_size := Main.document_size + + var delta_x := 0.0 + var delta_y := 0.0 + match anchor: + PaintLayer.ScaleAnchor.TopLeft: + pass + PaintLayer.ScaleAnchor.TopCenter: + delta_x = (new_document_size.x - prev_document_size.x) / 2 + PaintLayer.ScaleAnchor.TopRight: + delta_x = new_document_size.x - prev_document_size.x + PaintLayer.ScaleAnchor.Left: + delta_y = (new_document_size.y - prev_document_size.y) / 2 + PaintLayer.ScaleAnchor.Center: + delta_x = (new_document_size.x - prev_document_size.x) / 2 + delta_y = (new_document_size.y - prev_document_size.y) / 2 + PaintLayer.ScaleAnchor.Right: + delta_x = new_document_size.x - prev_document_size.x + delta_y = (new_document_size.y - prev_document_size.y) / 2 + PaintLayer.ScaleAnchor.BottomLeft: + delta_y = new_document_size.y - prev_document_size.y + PaintLayer.ScaleAnchor.BottomCenter: + delta_x = (new_document_size.x - prev_document_size.x) / 2 + delta_y = new_document_size.y - prev_document_size.y + PaintLayer.ScaleAnchor.BottomRight: + delta_x = new_document_size.x - prev_document_size.x + delta_y = new_document_size.y - prev_document_size.y + + start_point += Vector2(delta_x, delta_y) + end_point += Vector2(delta_x, delta_y) diff --git a/godot/src/material/paint_material.gd b/godot/src/material/paint_material.gd new file mode 100644 index 0000000..8ea6105 --- /dev/null +++ b/godot/src/material/paint_material.gd @@ -0,0 +1,3 @@ +## PaintMaterialの抽象クラス。 +class_name PaintMaterial +extends RefCounted diff --git a/godot/src/material/radial_gradient_paint_material.gd b/godot/src/material/radial_gradient_paint_material.gd new file mode 100644 index 0000000..708c76d --- /dev/null +++ b/godot/src/material/radial_gradient_paint_material.gd @@ -0,0 +1,63 @@ +class_name RadialGradientPaintMaterial +extends PaintMaterial + + +## グラデーション。 +var gradient: Gradient = Gradient.new() +## グラデーションのテクスチャ。 +var gradient_texture: GradientTexture1D = GradientTexture1D.new() + +## グラデーションの中心点。 +var center_point: Vector2 = Vector2(0, 0) +## グラデーションのハンドル1。 +var handle_1_point: Vector2 = Vector2(1, 0) +## グラデーションのハンドル2。 +var handle_2_point: Vector2 = Vector2(0, 1) + + +func _init() -> void: + gradient_texture.set_gradient(gradient) + gradient_texture.set_width(512) + + gradient.colors = PackedColorArray([Color(1, 1, 1, 1), Color(1, 0, 0, 1), Color(0, 0, 0, 1)]) + gradient.offsets = PackedFloat32Array([0, 0.5, 1]) + + var quater_size := Main.document_size / 4 + center_point = quater_size * 2 + handle_1_point = quater_size * 2 + Vector2(quater_size.x, 0) + handle_2_point = quater_size * 2 - Vector2(0, quater_size.y) + + +## ドキュメントサイズの変更を適用する。 +func change_document_size(new_document_size: Vector2, anchor: PaintLayer.ScaleAnchor) -> void: + var prev_document_size := Main.document_size + + var delta_x := 0.0 + var delta_y := 0.0 + match anchor: + PaintLayer.ScaleAnchor.TopLeft: + pass + PaintLayer.ScaleAnchor.TopCenter: + delta_x = (new_document_size.x - prev_document_size.x) / 2 + PaintLayer.ScaleAnchor.TopRight: + delta_x = new_document_size.x - prev_document_size.x + PaintLayer.ScaleAnchor.Left: + delta_y = (new_document_size.y - prev_document_size.y) / 2 + PaintLayer.ScaleAnchor.Center: + delta_x = (new_document_size.x - prev_document_size.x) / 2 + delta_y = (new_document_size.y - prev_document_size.y) / 2 + PaintLayer.ScaleAnchor.Right: + delta_x = new_document_size.x - prev_document_size.x + delta_y = (new_document_size.y - prev_document_size.y) / 2 + PaintLayer.ScaleAnchor.BottomLeft: + delta_y = new_document_size.y - prev_document_size.y + PaintLayer.ScaleAnchor.BottomCenter: + delta_x = (new_document_size.x - prev_document_size.x) / 2 + delta_y = new_document_size.y - prev_document_size.y + PaintLayer.ScaleAnchor.BottomRight: + delta_x = new_document_size.x - prev_document_size.x + delta_y = new_document_size.y - prev_document_size.y + + center_point += Vector2(delta_x, delta_y) + handle_1_point += Vector2(delta_x, delta_y) + handle_2_point += Vector2(delta_x, delta_y) diff --git a/godot/src/node/gradient_editor.gd b/godot/src/node/gradient_editor.gd new file mode 100644 index 0000000..3331b83 --- /dev/null +++ b/godot/src/node/gradient_editor.gd @@ -0,0 +1,180 @@ +class_name GradientEditor +extends VBoxContainer + + +const _color_allow_scene: PackedScene = preload("res://scenes/node/gradient_editor_color_arrow.tscn") + +@onready var _color_rect: ColorRect = %ColorRect +@onready var _color_picker: ColorPicker = %ColorPicker + +## グラデーションが変更したときのシグナル。 +signal on_gradient_changed(gradient: Gradient) + +## グラデーション。 +var _gradient: Gradient = Gradient.new() +## グラデーションテクスチャ。 +var _gradient_texture: GradientTexture1D = GradientTexture1D.new() + +## カラーアローのリスト。 +var _color_arrows: Array[GradientEditorColorArrow] = [] + +## 現在選択中のカラーアロー。 +var _selected_color_arrow: GradientEditorColorArrow = null + +## カラーアローを押下しているかどうか。 +var _color_arrow_pressing: bool = false + +## 更新が必要かどうか。 +var _need_update: bool = false + + +func _ready() -> void: + _color_rect.material = _color_rect.material.duplicate() + + +func _process(_delta: float) -> void: + if _need_update: + _gradient = Gradient.new() + + _color_arrows.sort_custom(func(a: GradientEditorColorArrow, b: GradientEditorColorArrow) -> bool: + return a.offset < b.offset + ) + + var colors: Array[Color] = [] + var offsets: Array[float] = [] + + for arrow in _color_arrows: + colors.push_back(arrow.color) + offsets.push_back(arrow.offset) + arrow.position.x = arrow.offset * _color_rect.size.x - 12 + if arrow == _selected_color_arrow: + arrow.select() + else: + arrow.deselect() + + _gradient.colors = colors + _gradient.offsets = offsets + _gradient_texture.gradient = _gradient + + (_color_rect.material as ShaderMaterial).set_shader_parameter("gradient_texture", _gradient_texture) + + on_gradient_changed.emit(_gradient) + + _need_update = false + + +func _input(event: InputEvent) -> void: + if event is InputEventMouseButton: + var mouse_event := event as InputEventMouseButton + + # mouse down + if not _color_arrow_pressing and mouse_event.pressed and mouse_event.button_index == MOUSE_BUTTON_LEFT: + for arrow in _color_arrows: + if arrow.hover: + if _selected_color_arrow != null: + _selected_color_arrow.deselect() + + _selected_color_arrow = arrow + _selected_color_arrow.select() + _color_picker.color = _selected_color_arrow.color + + _color_arrow_pressing = true + break + # mouse up + elif _color_arrow_pressing and not mouse_event.pressed: + _color_arrow_pressing = false + _need_update = true + + if event is InputEventMouseMotion: + # mouse drag + if _color_arrow_pressing: + var mouse_pos := _color_rect.get_local_mouse_position() + var offset := mouse_pos.x / _color_rect.size.x + offset = clamp(offset, 0.0, 1.0) + _selected_color_arrow.offset = offset + _need_update = true + + +## グラデーションを設定する。 +func set_gradient(gradient: Gradient) -> void: + _gradient = gradient.duplicate() + _gradient_texture.gradient = _gradient + + for arrow in _color_arrows: + arrow.queue_free() + _color_arrows.clear() + + for index in _gradient.colors.size(): + var arrow := _color_allow_scene.instantiate() as GradientEditorColorArrow + _color_rect.add_child(arrow) + _color_arrows.push_back(arrow) + arrow.color = _gradient.colors[index] + arrow.offset = _gradient.offsets[index] + + if _color_arrows.size() > 0: + _selected_color_arrow = _color_arrows[0] + _color_picker.color = _selected_color_arrow.color + + _need_update = true + + +## カラーアローを削除する。 +func remove_color_arrow() -> void: + for arrow in _color_arrows: + arrow.queue_free() + _color_arrows.clear() + + +## カラーピッカーの色が変更されたときに呼ばれるコールバック。 +func _on_color_picker_color_changed(color: Color) -> void: + if _selected_color_arrow != null: + _selected_color_arrow.color = color + _need_update = true + + +## プラスボタンが押下されたときに呼ばれるコールバック。 +func _on_plus_button_pressed() -> void: + if _color_arrows.size() == 0: + var arrow := _color_allow_scene.instantiate() as GradientEditorColorArrow + _color_rect.add_child(arrow) + _color_arrows.push_back(arrow) + elif _color_arrows.size() == 1: + var exists_arrow_offset := _color_arrows[0].offset + var arrow := _color_allow_scene.instantiate() as GradientEditorColorArrow + _color_rect.add_child(arrow) + _color_arrows.push_back(arrow) + arrow.color = Color(1, 1, 1, 1) + arrow.offset = 0.0 if exists_arrow_offset > 0.5 else 1.0 + else: + var offset := 1.0 + var next_select_arrow: GradientEditorColorArrow = null + for index in _color_arrows.size() - 1: + if _color_arrows[index] == _selected_color_arrow: + next_select_arrow = _color_arrows[index + 1] + break + if next_select_arrow == null: + var prev_select_arrow: GradientEditorColorArrow = null + for index in range(1, _color_arrows.size()): + if _color_arrows[index] == _selected_color_arrow: + prev_select_arrow = _color_arrows[index - 1] + break + offset = (_selected_color_arrow.offset + prev_select_arrow.offset) / 2 + else: + offset = (_selected_color_arrow.offset + next_select_arrow.offset) / 2 + var arrow := _color_allow_scene.instantiate() as GradientEditorColorArrow + _color_rect.add_child(arrow) + _color_arrows.push_back(arrow) + arrow.color = Color(1, 1, 1, 1) + arrow.offset = offset + + _need_update = true + + +## マイナスボタンが押下されたときに呼ばれるコールバック。 +func _on_minus_button_pressed() -> void: + if _color_arrows.size() > 1: + _selected_color_arrow.queue_free() + _color_arrows.erase(_selected_color_arrow) + _selected_color_arrow = _color_arrows[0] + + _need_update = true diff --git a/godot/src/node/gradient_editor_color_arrow.gd b/godot/src/node/gradient_editor_color_arrow.gd new file mode 100644 index 0000000..6afe56d --- /dev/null +++ b/godot/src/node/gradient_editor_color_arrow.gd @@ -0,0 +1,45 @@ +class_name GradientEditorColorArrow +extends Panel + + +@onready var _color_rect: ColorRect = %ColorRect; +@onready var _sprite: Sprite2D = %Sprite2D; + + +## 色。 +var color: Color = Color(0, 0, 0, 1): + set(value): + color = value + _color_rect.color = value + get: + return color + +## 色のオフセット。 +var offset: float = 0.0 + +## ホバー状態。 +var hover: bool = false + + +func _ready() -> void: + _sprite.material = _sprite.material.duplicate() + + +## 選択する。 +func select() -> void: + (_sprite.material as ShaderMaterial).set_shader_parameter("fill_color", Color(0.5, 0.5, 0.8, 1)) + + +## 選択を解除する。 +func deselect() -> void: + (_sprite.material as ShaderMaterial).set_shader_parameter("fill_color", Color(0.1, 0.1, 0.1, 1)) + + +## マウスがエリアに入ったときに呼ばれるコールバック。 +func _on_mouse_entered() -> void: + hover = true + + +## マウスがエリアから出たときに呼ばれるコールバック。 +func _on_mouse_exited() -> void: + hover = false diff --git a/godot/src/node/layer_list_item.gd b/godot/src/node/layer_list_item.gd new file mode 100644 index 0000000..4a5e08a --- /dev/null +++ b/godot/src/node/layer_list_item.gd @@ -0,0 +1,660 @@ +## レイヤーリストの要素のノードのクラス。 +class_name LayerListItem +extends PanelContainer + + +@onready var _node: PanelContainer = $"." + +@onready var _eye_open: Sprite2D = %EyeButton/EyeOpen +@onready var _eye_slash: Sprite2D = %EyeButton/EyeSlash + +@onready var _pen_panel: Panel = %PenPanel +@onready var _pen_sprite: Sprite2D = %PenPanel/Sprite2D +@onready var _locked_panel: Panel = %LockedPanel +@onready var _locked_sprite: Sprite2D = %LockedPanel/Sprite2D + +@onready var _spacer: ColorRect = %Spacer + +@onready var _clipping_rect: ColorRect = %ClippingRect + +@onready var _collapse_button: Button = %CollapseButton +@onready var _folder_open: Sprite2D = %CollapseButton/FolderOpen +@onready var _folder_close: Sprite2D = %CollapseButton/FolderClose +@onready var _caret_open: Sprite2D = %CollapseButton/CaretOpen +@onready var _caret_close: Sprite2D = %CollapseButton/CaretClose +@onready var _fill_mark: Sprite2D = %CollapseButton/FillMark + +@onready var _thumbnail_panel: Panel = %Thumbnail +@onready var _thumbnail_rect: ColorRect = %Thumbnail/ThumbnailRect + +@onready var _blend_mode_text: Label = %BlendModeText +@onready var _alpha_text: Label = %AlphaText + +@onready var _boolean_panel: Panel = %BooleanPanel +@onready var _boolean_union: Sprite2D = %BooleanPanel/Union +@onready var _boolean_diff: Sprite2D = %BooleanPanel/Diff +@onready var _boolean_intersect: Sprite2D = %BooleanPanel/Intersect +@onready var _boolean_xor: Sprite2D = %BooleanPanel/Xor + +@onready var _name_text: LineEdit = %NameText + +@onready var _missint_panel: Panel = %MissingPanel + +@onready var _dragging_panel: Panel = $DraggingtPanel + +## マウスオーバーしているかどうか。 +var mouse_over: bool = false +## 上半分にマウスオーバーしているかどうか。 +var mouse_over_top: bool = false +## 下半分にマウスオーバーしているかどうか。 +var mouse_over_bottom: bool = false +## インデントのデプス。 +var depth: int = 0 +## ドラッグ中かどうか。 +var dragging: bool = false + +## 編集中のテキスト。 +var _editing_name: String = "" +## 選択中かどうか。 +var _selected: bool = false +## 要素の上でマウスダウンしているかどうか。 +var _mouse_down: bool = false + +## eye openのSprite2Dのマテリアル +var _eye_open_material: ShaderMaterial +## eye closeのSprite2Dのマテリアル +var _eye_slash_material: ShaderMaterial +## lockedのSprite2Dのマテリアル +var _locked_material: ShaderMaterial +## thumbnail rectのColorRectのマテリアル +var _thumbnail_rect_material: ShaderMaterial + +## このアイテムの対応するレイヤー。 +var _layer: PaintLayer = null +## このアイテムの対応するパス。 +var _path: Path = null + +## パス再計算後にcommit hisotryする必要があるかどうか。 +var _need_commit_history: bool = false + +## リストアイテムのシーン。 +static var _list_item: PackedScene + + +func _ready() -> void: + _node.add_theme_stylebox_override("panel", _node.get_theme_stylebox("panel").duplicate() as StyleBox) + _name_text.add_theme_stylebox_override("focus", _name_text.get_theme_stylebox("focus").duplicate() as StyleBox) + _eye_open_material = _eye_open.material.duplicate() as ShaderMaterial + _eye_open.material = _eye_open_material + _eye_slash_material = _eye_slash.material.duplicate() as ShaderMaterial + _eye_slash.material = _eye_slash_material + _locked_material = _locked_sprite.material.duplicate() as ShaderMaterial + _locked_sprite.material = _locked_material + _thumbnail_rect_material = _thumbnail_rect.material.duplicate() as ShaderMaterial + _thumbnail_rect.material = _thumbnail_rect_material + + +func _process(_delta: float) -> void: + if _need_commit_history: + Main.commit_history() + _need_commit_history = false + + +func _input(event: InputEvent) -> void: + if event is InputEventMouseButton: + var mouse_event := event as InputEventMouseButton + var mouse_position := _node.get_local_mouse_position() + + # アイテムがクリックされたら選択して、アイテム外がクリックされたらtextを編集状態解除する + var rect := Rect2(Vector2.ZERO, size) + if mouse_event.button_index == MOUSE_BUTTON_LEFT: + if rect.has_point(mouse_position) and mouse_over: + if mouse_event.pressed and not _mouse_down: + _mouse_down = true + if not mouse_event.pressed and _mouse_down: + _mouse_down = false + # 他のアイテムのrename確定処理が終わってからclickedを呼び出す + if mouse_event.ctrl_pressed and mouse_event.shift_pressed: + if _layer: + Main.layers.add_selected_layer_range(_layer) + Main.commit_history() + elif _path: + Main.layers.add_selected_path_range(_path) + Main.commit_history() + elif mouse_event.ctrl_pressed: + if _layer: + Main.layers.add_or_remove_selected_layer(_layer) + Main.commit_history() + elif _path: + Main.layers.add_or_remove_selected_path(_path) + Main.commit_history() + elif mouse_event.shift_pressed: + if _layer: + Main.layers.set_selected_layer_range(_layer) + Main.commit_history() + elif _path: + Main.layers.set_selected_path_range(_path) + Main.commit_history() + else: + if _layer: + Main.layers.set_selected_layer(_layer) + Main.commit_history() + elif _path: + Main.layers.set_selected_path(_path) + Main.commit_history() + else: + _exit_edit_name() + + # ダブルクリックで名前変更 + var text_mouse_position := _name_text.get_local_mouse_position() + var text_rect := Rect2(Vector2.ZERO, _name_text.size) + if text_rect.has_point(text_mouse_position) and mouse_event.button_index == MOUSE_BUTTON_LEFT and mouse_event.double_click: + _enter_edit_name() + + +## テキスト編集状態に入る。 +func _enter_edit_name() -> void: + var style_box := _name_text.get_theme_stylebox("focus") as StyleBoxFlat + _name_text.editable = true + _name_text.selecting_enabled = true + style_box.border_width_top = 2 + style_box.border_width_bottom = 2 + style_box.border_width_left = 2 + style_box.border_width_right = 2 + _editing_name = _name_text.text + + +## テキストを編集状態解除する。 +func _exit_edit_name() -> void: + if _name_text.editable: + if _path: + _path.path_name = _editing_name + if _layer: + _layer.layer_name = _editing_name + var style_box := _name_text.get_theme_stylebox("focus") as StyleBoxFlat + _name_text.editable = false + _name_text.selecting_enabled = false + style_box.border_width_top = 0 + style_box.border_width_bottom = 0 + style_box.border_width_left = 0 + style_box.border_width_right = 0 + + +## このItemで表示するレイヤーを指定する。 +func _set_layer(layer: PaintLayer) -> void: + _layer = layer + _path = null + + # eyeアイコン + if layer.visible: + _eye_open.visible = true + _eye_slash.visible = false + else: + _eye_open.visible = false + _eye_slash.visible = true + if layer.parent_visible: + _eye_open_material.set_shader_parameter("fill_color", Color("cccccc")) + _eye_slash_material.set_shader_parameter("fill_color", Color("cccccc")) + else: + _eye_open_material.set_shader_parameter("fill_color", Color("666666")) + _eye_slash_material.set_shader_parameter("fill_color", Color("666666")) + + # ロック状態 + if layer.is_locked(): + _locked_panel.visible = true + _pen_panel.visible = false + else: + _locked_panel.visible = false + _pen_panel.visible = true + if layer.locked: + _locked_material.set_shader_parameter("fill_color", Color("cccccc")) + else: + _locked_material.set_shader_parameter("fill_color", Color("666666")) + + + # クリッピング + if layer.clipped: + _clipping_rect.visible = true + else: + _clipping_rect.visible = false + + # サムネイル + if Main.document_size.x > Main.document_size.y: + _thumbnail_rect.size.x = _thumbnail_panel.size.x + _thumbnail_rect.size.y = _thumbnail_panel.size.y * Main.document_size.y / Main.document_size.x + _thumbnail_rect.position = Vector2(0, (38 - _thumbnail_rect.size.y) / 2) + else: + _thumbnail_rect.size.x = _thumbnail_panel.size.x * Main.document_size.x / Main.document_size.y + _thumbnail_rect.size.y = _thumbnail_panel.size.y + _thumbnail_rect.position = Vector2((38 - _thumbnail_rect.size.x) / 2, 0) + + # blend mode + if layer.blend_mode == PaintLayer.BlendMode.Normal: + _blend_mode_text.text = tr("BLEND_MODE_NORMAL") + elif layer.blend_mode == PaintLayer.BlendMode.Add: + _blend_mode_text.text = tr("BLEND_MODE_ADD") + elif layer.blend_mode == PaintLayer.BlendMode.Multiply: + _blend_mode_text.text = tr("BLEND_MODE_MULTIPLY") + elif layer.blend_mode == PaintLayer.BlendMode.Screen: + _blend_mode_text.text = tr("BLEND_MODE_SCREEN") + elif layer.blend_mode == PaintLayer.BlendMode.Overlay: + _blend_mode_text.text = tr("BLEND_MODE_OVERLAY") + + # alpha + _alpha_text.visible = true + _alpha_text.text = str(layer.alpha) + "%" + + # boolean + _boolean_panel.visible = false + + # レイヤーの名前 + _name_text.text = layer.layer_name + + # 選択中かどうか + var style_box := _node.get_theme_stylebox("panel") as StyleBoxFlat + var selected := false + for l in Main.layers.selected_layers: + if l == layer: + selected = true + break + if selected: + style_box.bg_color = Color(0.5, 0.8, 0.9, 0.25) + else: + style_box.bg_color = Color.TRANSPARENT + + if layer is PathPaintLayer: + var path_layer := layer as PathPaintLayer + + # ペンアイコン + if path_layer == Main.layers.drawing_path_layer: + _pen_sprite.visible = true + else: + _pen_sprite.visible = false + + # デプス + _spacer.custom_minimum_size.x = 24 * depth + + # サムネイル + if Main.compositor.composite_textures[path_layer.id].valid: + _thumbnail_rect_material.set_shader_parameter("main_texture", Main.compositor.composite_textures[path_layer.id].texture) + else: + _thumbnail_rect_material.set_shader_parameter("main_texture", null) + + # 折りたたみ + _collapse_button.visible = true + _folder_close.visible = false + _folder_open.visible = false + _fill_mark.visible = false + if path_layer.collapsed: + _caret_open.visible = false + _caret_close.visible = true + else: + _caret_open.visible = true + _caret_close.visible = false + + # マテリアルmissing + if path_layer.is_material_missing(): + _missint_panel.visible = true + else: + _missint_panel.visible = false + + elif layer is FillPaintLayer: + var fill_layer := layer as FillPaintLayer + + # ペンアイコン + _pen_sprite.visible = false + + # デプス + _spacer.custom_minimum_size.x = 24 * depth + + # サムネイル + if Main.compositor.composite_textures[fill_layer.id].valid: + _thumbnail_rect_material.set_shader_parameter("main_texture", Main.compositor.composite_textures[fill_layer.id].texture) + else: + _thumbnail_rect_material.set_shader_parameter("main_texture", null) + + # 折りたたみみ + _collapse_button.visible = true + _folder_close.visible = false + _folder_open.visible = false + _caret_open.visible = false + _caret_close.visible = false + _fill_mark.visible = true + + # マテリアルmissing + if fill_layer.is_material_missing(): + _missint_panel.visible = true + else: + _missint_panel.visible = false + + elif layer is GroupPaintLayer: + var group_layer := layer as GroupPaintLayer + + # ペンアイコン + _pen_sprite.visible = false + + # デプス + _spacer.custom_minimum_size.x = 24 * depth + + # サムネイル + if Main.compositor.composite_textures[group_layer.id].valid: + _thumbnail_rect_material.set_shader_parameter("main_texture", Main.compositor.composite_textures[group_layer.id].texture) + else: + _thumbnail_rect_material.set_shader_parameter("main_texture", null) + + # 折りたたみ + _collapse_button.visible = true + _caret_open.visible = false + _caret_close.visible = false + _fill_mark.visible = false + if group_layer.collapsed: + _folder_open.visible = false + _folder_close.visible = true + else: + _folder_open.visible = true + _folder_close.visible = false + + # マテリアルmissing + _missint_panel.visible = false + + +## このItemで表示するパスを指定する。 +func _set_path(path: Path) -> void: + _layer = null + _path = path + + # eyeアイコン + if path.visible: + _eye_open.visible = true + _eye_slash.visible = false + else: + _eye_open.visible = false + _eye_slash.visible = true + if path.parent_visible: + _eye_open_material.set_shader_parameter("fill_color", Color("cccccc")) + _eye_slash_material.set_shader_parameter("fill_color", Color("cccccc")) + else: + _eye_open_material.set_shader_parameter("fill_color", Color("666666")) + _eye_slash_material.set_shader_parameter("fill_color", Color("666666")) + + # ロック状態 + if path.is_locked(): + _locked_panel.visible = true + _pen_panel.visible = false + else: + _locked_panel.visible = false + _pen_panel.visible = true + if path.locked: + _locked_material.set_shader_parameter("fill_color", Color("cccccc")) + else: + _locked_material.set_shader_parameter("fill_color", Color("666666")) + + # ペンアイコン + _pen_sprite.visible = false + + # クリッピング + _clipping_rect.visible = false + + # デプス + _spacer.custom_minimum_size.x = 24 + 24 * depth + + # 折りたたみ + _collapse_button.visible = false + + # サムネイル + if Main.document_size.x > Main.document_size.y: + _thumbnail_rect.size.x = _thumbnail_panel.size.x + _thumbnail_rect.size.y = _thumbnail_panel.size.y * Main.document_size.y / Main.document_size.x + _thumbnail_rect.position = Vector2(0, (38 - _thumbnail_rect.size.y) / 2) + else: + _thumbnail_rect.size.x = _thumbnail_panel.size.x * Main.document_size.x / Main.document_size.y + _thumbnail_rect.size.y = _thumbnail_panel.size.y + _thumbnail_rect.position = Vector2((38 - _thumbnail_rect.size.x) / 2, 0) + _thumbnail_rect_material.set_shader_parameter("main_texture", path.get_path_texture()) + + # blend mode + _blend_mode_text.text = "" + + # alpha + _alpha_text.visible = false + + # boolean + if path.get_is_line(): + _boolean_panel.visible = false + else: + _boolean_panel.visible = true + if path.boolean == Path.Boolean.Union: + _boolean_union.visible = true + _boolean_diff.visible = false + _boolean_intersect.visible = false + _boolean_xor.visible = false + elif path.boolean == Path.Boolean.Diff: + _boolean_union.visible = false + _boolean_diff.visible = true + _boolean_intersect.visible = false + _boolean_xor.visible = false + elif path.boolean == Path.Boolean.Intersect: + _boolean_union.visible = false + _boolean_diff.visible = false + _boolean_intersect.visible = true + _boolean_xor.visible = false + elif path.boolean == Path.Boolean.Xor: + _boolean_union.visible = false + _boolean_diff.visible = false + _boolean_intersect.visible = false + _boolean_xor.visible = true + + # パスの名前 + _name_text.text = path.path_name + + # 選択中かどうか + var style_box := _node.get_theme_stylebox("panel") as StyleBoxFlat + var selected := false + for p in Main.layers.selected_paths: + if p == path: + selected = true + break + if selected: + style_box.bg_color = Color(0.5, 0.8, 0.9, 0.25) + else: + style_box.bg_color = Color.TRANSPARENT + + # マテリアルmissing + _missint_panel.visible = false + + +## 選択中の表示を設定する。 +func set_selected(selected: bool) -> void: + var style_box := _node.get_theme_stylebox("panel") as StyleBoxFlat + if selected: + style_box.bg_color = Color(0.5, 0.8, 0.9, 0.25) + else: + style_box.bg_color = Color.TRANSPARENT + _selected = selected + + +## ドラッグ中の表示を設定する。 +func set_dragging(new_dragging: bool) -> void: + _dragging_panel.visible = new_dragging + dragging = new_dragging + + +## 現在のアイテムがパスかどうかを返す。 +func is_path() -> bool: + if _path is Path: + return true + return false + + +## 現在のアイテムがPathPaintLayerかどうかを返す。 +func is_path_layer() -> bool: + if _layer is PathPaintLayer: + return true + return false + + +## 現在のアイテムがPathPaintLayerが空かどうかを返す。 +func is_path_layer_empty() -> bool: + if _layer is PathPaintLayer: + var path_layer := _layer as PathPaintLayer + return path_layer.paths.size() == 0 + return false + + +## 現在のアイテムがFillPaintLayerかどうかを返す。 +func is_fill_layer() -> bool: + if _layer is FillPaintLayer: + return true + return false + + +## 現在のアイテムがGroupPaintLayerかどうかを返す。 +func is_group_layer() -> bool: + if _layer is GroupPaintLayer: + return true + return false + + +## 現在のアイテムがGroupPaintLayerが空かどうかを返す。 +func is_group_layer_empty() -> bool: + if _layer is GroupPaintLayer: + var group_layer := _layer as GroupPaintLayer + return group_layer.child_layers.size() == 0 + return false + + +## 現在のアイテムがPathPaintLayerだったらそれを返す。 +## そうでなければnullを返す。 +func get_path_layer() -> PathPaintLayer: + if _layer is PathPaintLayer: + var gl := _layer as PathPaintLayer + return gl + return null + + +## 現在のアイテムがGroupPaintLayerだったらそれを返す。 +## そうでなければnullを返す。 +func get_group_layer() -> GroupPaintLayer: + if _layer is GroupPaintLayer: + var gl := _layer as GroupPaintLayer + return gl + return null + + +## 現在のアイテムがPathPaintLayerもしくはGroupPaintLayerでopen状態かどうか +func is_open() -> bool: + if _layer is PathPaintLayer: + var path_layer := _layer as PathPaintLayer + return not path_layer.collapsed + if _layer is GroupPaintLayer: + var group_layer := _layer as GroupPaintLayer + return not group_layer.collapsed + return false + + +## 名前のテキストを確定したときに呼ばれるコールバック。 +func _on_line_edit_text_submitted(_new_text: String) -> void: + _exit_edit_name() + + +## 名前のテキストを変更したときに呼ばれるコールバック。 +func _on_line_edit_text_changed(new_text: String) -> void: + _editing_name = new_text + + +## マウスオーバーしたときに呼び出されるコールバック。 +func _on_mouse_entered() -> void: + mouse_over = true + var mouse_pos := get_local_mouse_position() + if mouse_pos.y > size.y / 2: + mouse_over_bottom = true + mouse_over_top = false + else: + mouse_over_bottom = false + mouse_over_top = true + + +## マウスが外れたときに呼び出されるコールバック。 +func _on_mouse_exited() -> void: + mouse_over = false + mouse_over_bottom = false + mouse_over_top = false + + +## 折りたたみボタンを押したときに呼び出されるコールバック。 +func _on_collapse_button_button_up() -> void: + if _layer is PathPaintLayer: + var path_layer := _layer as PathPaintLayer + path_layer.collapsed = not path_layer.collapsed + _collapse_button.visible = true + _folder_close.visible = false + _folder_open.visible = false + _fill_mark.visible = false + if path_layer.collapsed: + _caret_open.visible = false + _caret_close.visible = true + else: + _caret_open.visible = true + _caret_close.visible = false + elif _layer is GroupPaintLayer: + var group_layer := _layer as GroupPaintLayer + group_layer.collapsed = not group_layer.collapsed + _collapse_button.visible = true + _caret_open.visible = false + _caret_close.visible = false + _fill_mark.visible = false + if group_layer.collapsed: + _folder_open.visible = false + _folder_close.visible = true + else: + _folder_open.visible = true + _folder_close.visible = false + Main.emit_update_layer_list_tab() + + +## 表示非表示の切り替えボタンを押したときに呼び出されるコールバック +func _on_eye_button_button_up() -> void: + if _layer is PathPaintLayer: + var path_layer := _layer as PathPaintLayer + path_layer.update_visible(not path_layer.visible) + if _layer is FillPaintLayer: + var fill_layer := _layer as FillPaintLayer + fill_layer.update_visible(not fill_layer.visible) + if _layer is GroupPaintLayer: + var group_layer := _layer as GroupPaintLayer + group_layer.update_visible(not group_layer.visible) + if _path: + _path.visible = not _path.visible + _path.recalculate_polygon() + Main.emit_update_layer_list_tab() + _need_commit_history = true + + +## レイヤーとして初期化する +func init_layer(layer: PaintLayer, new_depth: int) -> void: + depth = new_depth + _set_layer(layer) + dragging = false + for dragging_layer in Main.layers.dragging_layers: + if layer == dragging_layer: + dragging = true + set_dragging(dragging) + + +## パスとして初期化する +func init_path(path: Path, new_depth: int) -> void: + depth = new_depth + _set_path(path) + dragging = false + for dragging_path in Main.layers.dragging_paths: + if path == dragging_path: + dragging = true + set_dragging(dragging) + + +## ノードを生成する。 +static func new_item() -> LayerListItem: + if not _list_item: + _list_item = load("res://scenes/node/layer_list_item.tscn") + var list_item := _list_item.instantiate() as LayerListItem + return list_item diff --git a/godot/src/node/linear_gradient_picker.gd b/godot/src/node/linear_gradient_picker.gd new file mode 100644 index 0000000..f50b979 --- /dev/null +++ b/godot/src/node/linear_gradient_picker.gd @@ -0,0 +1,73 @@ +class_name LinearGradientPicker +extends Popup + + +@onready var _start_x: SpinBox = %StartX +@onready var _start_y: SpinBox = %StartY +@onready var _end_x: SpinBox = %EndX +@onready var _end_y: SpinBox = %EndY +@onready var _gradient_editor: GradientEditor = %GradientEditor + +## グラデーションが変更したときのシグナル。 +signal on_gradient_changed(gradient_material: LinearGradientPaintMaterial) + +## 現在編集中のマテリアル。 +var _gradient_material: LinearGradientPaintMaterial + + +func _ready() -> void: + _gradient_editor.on_gradient_changed.connect(_on_gradient_editor_on_gradient_changed) + + +## ピッカーを表示する。 +func show_picker(gradient_material: LinearGradientPaintMaterial) -> void: + _gradient_editor.set_gradient(gradient_material.gradient) + _start_x.set_value_no_signal(gradient_material.start_point.x) + _start_y.set_value_no_signal(gradient_material.start_point.y) + _end_x.set_value_no_signal(gradient_material.end_point.x) + _end_y.set_value_no_signal(gradient_material.end_point.y) + _gradient_material = gradient_material + show() + + +## グラデーションが変化したときのコールバック。 +func _on_gradient_editor_on_gradient_changed(gradient: Gradient) -> void: + _gradient_material.gradient = gradient + _gradient_material.gradient_texture.gradient = gradient + on_gradient_changed.emit(_gradient_material) + Main.on_change_material_parameters_changed.emit() + + +## ピッカーが非表示になったときに呼び出されるコールバック。 +func _on_popup_hide() -> void: + _gradient_editor.remove_color_arrow() + _gradient_material = null + + +## 開始点のXの値が変わったときのコールバック。 +func _on_start_x_value_changed(value: float) -> void: + _gradient_material.start_point.x = value + Main.on_change_material_parameters_changed.emit() + + +## 開始点のYの値が変わったときのコールバック。 +func _on_start_y_value_changed(value: float) -> void: + _gradient_material.start_point.y = value + Main.on_change_material_parameters_changed.emit() + + +## 終了点のXの値が変わったときのコールバック。 +func _on_end_x_value_changed(value: float) -> void: + _gradient_material.end_point.x = value + Main.on_change_material_parameters_changed.emit() + + +## 終了点のYの値が変わったときのコールバック。 +func _on_end_y_value_changed(value: float) -> void: + _gradient_material.end_point.y = value + Main.on_change_material_parameters_changed.emit() + + +## 編集ボタンが押されたときのコールバック。 +func _on_edit_button_pressed() -> void: + Main.emit_start_edit_linear_gradient(_gradient_material) diff --git a/godot/src/node/list_drop_cursor.gd b/godot/src/node/list_drop_cursor.gd new file mode 100644 index 0000000..7ef71a6 --- /dev/null +++ b/godot/src/node/list_drop_cursor.gd @@ -0,0 +1,11 @@ +## リストのDnDの先を示すカーソルのクラス。 +class_name ListDropCursor +extends BoxContainer + + +@onready var _spacing: ColorRect = $Control/ColorRect + + +## インデントを指定する。 +func set_depth(depth: int) -> void: + _spacing.position.x = 50 + depth * 32 diff --git a/godot/src/node/material_list_item.gd b/godot/src/node/material_list_item.gd new file mode 100644 index 0000000..59af97d --- /dev/null +++ b/godot/src/node/material_list_item.gd @@ -0,0 +1,278 @@ +## マテリアルリストの要素のノードのクラス。 +class_name MaterialListItem +extends PanelContainer + + +@onready var _node: PanelContainer = $"." +@onready var _color_panel: Panel = %Color +@onready var _name_text: LineEdit = %NameText +@onready var _popup: PopupPanel = $PopupPanel +@onready var _dragging_panel: Panel = $DraggingtPanel +@onready var _color_picker: ColorPicker = $PopupPanel/ColorPicker +@onready var _linear_gradient_picker: LinearGradientPicker = $LinearGradientPicker +@onready var _radial_gradient_picker: RadialGradientPicker = $RadialGradientPicker + +## この要素がクリックされたときに発火するシグナル。 +signal on_selected() + +## マウスオーバーしているかどうか。 +var mouse_over: bool = false +## 上半分にマウスオーバーしているかどうか。 +var mouse_over_top: bool = false +## 下半分にマウスオーバーしているかどうか。 +var mouse_over_bottom: bool = false +## このマテリアルのマテリアルid。 +var material_id: String = "" + +## 編集中のテキスト。 +var _editing_name: String = "" +## 名前を編集したかどうか。 +var _edited_name: bool = false +## 選択中かどうか。 +var _selected: bool = false + +## 色の操作中かどうか。 +var _is_manipulate_color: bool = false + +## マテリアルの単一色の表示マテリアル。 +@export var _color_material: ShaderMaterial +## マテリアルの線形グラデーションの表示マテリアル。 +@export var _linear_gradient_material: ShaderMaterial +## マテリアルの放射グラデーションの表示マテリアル。 +@export var _radial_gradient_material: ShaderMaterial + + +func _ready() -> void: + _node.add_theme_stylebox_override("panel", _node.get_theme_stylebox("panel").duplicate() as StyleBox) + _name_text.add_theme_stylebox_override("focus", _name_text.get_theme_stylebox("focus").duplicate() as StyleBox) + + +func _input(event: InputEvent) -> void: + if event is InputEventMouseButton: + var mouse_event := event as InputEventMouseButton + var mouse_position := _node.get_local_mouse_position() + + # アイテムがクリックされたら選択して、アイテム外がクリックされたらtextを編集状態解除する + var rect := Rect2(Vector2.ZERO, size) + if mouse_event.button_index == MOUSE_BUTTON_LEFT and mouse_event.pressed and mouse_over: + if rect.has_point(mouse_position): + if not _selected: + # 他のアイテムのrename確定処理が終わってからclickedを呼び出す + (func x() -> void: on_selected.emit()).call_deferred() + else: + _exit_edit_name() + + # ダブルクリックで名前変更 + var text_mouse_position := _name_text.get_local_mouse_position() + var text_rect := Rect2(Vector2.ZERO, _name_text.size) + if text_rect.has_point(text_mouse_position) and mouse_event.button_index == MOUSE_BUTTON_LEFT and mouse_event.double_click: + _enter_edit_name() + + +## テキスト編集状態に入る。 +func _enter_edit_name() -> void: + var style_box := _name_text.get_theme_stylebox("focus") as StyleBoxFlat + _name_text.editable = true + _name_text.selecting_enabled = true + style_box.border_width_top = 2 + style_box.border_width_bottom = 2 + style_box.border_width_left = 2 + style_box.border_width_right = 2 + _editing_name = _name_text.text + + +## テキストを編集状態解除する。 +func _exit_edit_name() -> void: + var style_box := _name_text.get_theme_stylebox("focus") as StyleBoxFlat + _name_text.editable = false + _name_text.selecting_enabled = false + style_box.border_width_top = 0 + style_box.border_width_bottom = 0 + style_box.border_width_left = 0 + style_box.border_width_right = 0 + if _selected: + Main.materials.rename_material(_editing_name) + if _edited_name: + _edited_name = false + Main.commit_history() + + +## このマテリアルアイテムに色を設定する。 +func set_color(color: Color) -> void: + var mat := _color_material.duplicate() as ShaderMaterial + mat.set_shader_parameter("fill_color", color) + _color_panel.material = mat + + +## このマテリアルアイテムに線形グラデーションを設定する。 +func set_linear_gradient(gradient_texture: GradientTexture1D) -> void: + var mat := _linear_gradient_material.duplicate() as ShaderMaterial + mat.set_shader_parameter("gradient_texture", gradient_texture) + _color_panel.material = mat + + +## このマテリアルアイテムに放射グラデーションを設定する。 +func set_radial_gradient(gradient_texture: GradientTexture1D) -> void: + var mat := _radial_gradient_material.duplicate() as ShaderMaterial + mat.set_shader_parameter("gradient_texture", gradient_texture) + _color_panel.material = mat + + +## マテリアルの名前を設定する。 +func set_material_name(new_name: String) -> void: + _name_text.text = new_name + _editing_name = _name_text.text + + +## 選択中の表示を設定する。 +func set_selected(selected: bool) -> void: + var style_box := _node.get_theme_stylebox("panel") as StyleBoxFlat + if selected: + style_box.bg_color = Color(0.5, 0.8, 0.9, 0.25) + else: + style_box.bg_color = Color.TRANSPARENT + _selected = selected + + +## ドラッグ中の表示を設定する。 +func set_dragging(dragging: bool) -> void: + _dragging_panel.visible = dragging + + +## 色の操作中かどうか。 +func is_manipulate_color() -> bool: + return _is_manipulate_color + + +## 色のボタンを押したときのコールバック。 +func _on_button_button_up() -> void: + var paint_material := Main.materials.get_material(material_id) + if paint_material is ColorPaintMaterial: + var color_material := paint_material as ColorPaintMaterial + _color_picker.color = color_material.color + _popup.show() + _is_manipulate_color = true + + var pos := _node.get_screen_position() + Vector2(0, 32) + var popup_size := _popup.get_visible_rect().size + + # get safe area + var safe_area := DisplayServer.get_display_safe_area() + + # clamp pos + if pos.x + popup_size.x > safe_area.position.x + safe_area.size.x: + pos.x = safe_area.position.x + safe_area.size.x - popup_size.x + if pos.y + popup_size.y > safe_area.position.y + safe_area.size.y: + pos.y = safe_area.position.y + safe_area.size.y - popup_size.y + + _popup.position = pos + elif paint_material is LinearGradientPaintMaterial: + var linear_gradient_material := paint_material as LinearGradientPaintMaterial + _linear_gradient_picker.show_picker(linear_gradient_material) + _is_manipulate_color = true + + var pos := _node.get_screen_position() + Vector2(0, 32) + var popup_size := _linear_gradient_picker.get_visible_rect().size + + # get safe area + var safe_area := DisplayServer.get_display_safe_area() + + # clamp pos + if pos.x + popup_size.x > safe_area.position.x + safe_area.size.x: + pos.x = safe_area.position.x + safe_area.size.x - popup_size.x + if pos.y + popup_size.y > safe_area.position.y + safe_area.size.y: + pos.y = safe_area.position.y + safe_area.size.y - popup_size.y + + _linear_gradient_picker.position = pos + elif paint_material is RadialGradientPaintMaterial: + var radial_gradient_material := paint_material as RadialGradientPaintMaterial + _radial_gradient_picker.show_picker(radial_gradient_material) + _is_manipulate_color = true + + var pos := _node.get_screen_position() + Vector2(0, 32) + var popup_size := _radial_gradient_picker.get_visible_rect().size + + # get safe area + var safe_area := DisplayServer.get_display_safe_area() + + # clamp pos + if pos.x + popup_size.x > safe_area.position.x + safe_area.size.x: + pos.x = safe_area.position.x + safe_area.size.x - popup_size.x + if pos.y + popup_size.y > safe_area.position.y + safe_area.size.y: + pos.y = safe_area.position.y + safe_area.size.y - popup_size.y + + _radial_gradient_picker.position = pos + +## カラーピッカーの色が変わったときに呼び出されるコールバック。 +func _on_color_picker_color_changed(color: Color) -> void: + var mat := _color_material.duplicate() as ShaderMaterial + mat.set_shader_parameter("fill_color", color) + _color_panel.material = mat + var paint_material := Main.materials.get_material(material_id) + if paint_material is ColorPaintMaterial: + var color_material := paint_material as ColorPaintMaterial + color_material.color = color + Main.on_change_material_parameters_changed.emit() + + +## 線形グラデーションピッカーのグラデーションが変わったときに呼び出されるコールバック。 +func _on_linear_gradient_picker_on_gradient_changed(gradient_material: LinearGradientPaintMaterial) -> void: + var mat := _linear_gradient_material.duplicate() as ShaderMaterial + mat.set_shader_parameter("gradient_texture", gradient_material.gradient_texture) + _color_panel.material = mat + + +## 放射グラデーションピッカーのグラデーションが変わったときに呼び出されるコールバック。 +func _on_radial_gradient_picker_on_gradient_changed(gradient_material: RadialGradientPaintMaterial) -> void: + var mat := _radial_gradient_material.duplicate() as ShaderMaterial + mat.set_shader_parameter("gradient_texture", gradient_material.gradient_texture) + _color_panel.material = mat + + +## 名前のテキストを確定したときに呼ばれるコールバック。 +func _on_line_edit_text_submitted(new_text: String) -> void: + Main.materials.rename_material(new_text) + _exit_edit_name() + + +## 名前のテキストを変更したときに呼ばれるコールバック。 +func _on_line_edit_text_changed(new_text: String) -> void: + _editing_name = new_text + _edited_name = true + + +## マウスオーバーしたときに呼び出されるコールバック。 +func _on_padding_mouse_entered() -> void: + mouse_over = true + var mouse_pos := get_local_mouse_position() + if mouse_pos.y > size.y / 2: + mouse_over_bottom = true + mouse_over_top = false + else: + mouse_over_bottom = false + mouse_over_top = true + + +## マウスが外れたときに呼び出されるコールバック。 +func _on_padding_mouse_exited() -> void: + mouse_over = false + mouse_over_bottom = false + mouse_over_top = false + + +## ポップアップが閉じたときのコールバック。 +func _on_popup_panel_popup_hide() -> void: + _is_manipulate_color = false + Main.commit_history() + + +## 線形グラデーションピッカーが閉じたときのコールバック。 +func _on_linear_gradient_picker_popup_hide() -> void: + _is_manipulate_color = false + Main.commit_history() + + +## 放射グラデーションピッカーが閉じたときのコールバック。 +func _on_radial_gradient_picker_popup_hide() -> void: + _is_manipulate_color = false + Main.commit_history() diff --git a/godot/src/node/material_select_popup.gd b/godot/src/node/material_select_popup.gd new file mode 100644 index 0000000..a83fc2a --- /dev/null +++ b/godot/src/node/material_select_popup.gd @@ -0,0 +1,67 @@ +## マテリアル選択ポップアップのクラス。 +class_name MaterialSelectPopup +extends Popup + + +@onready var _container: VBoxContainer = %ListContainer +@onready var _search: LineEdit = %LineEdit + +## マテリアル選択時のシグナル。 +## 引数はマテリアルのidの文字列。 +signal on_material_selected(material_id: String) + +## リストアイテムのシーン。 +const _list_item: PackedScene = preload("res://scenes/node/material_select_popup_item.tscn") + +## 検索文字列。 +var _search_text: String = "" + + +## マテリアルリストポップアップの中身を更新する。 +func _update_content() -> void: + for child in _container.get_children(): + child.queue_free() + + for index in Main.materials.list.size(): + var item := Main.materials.list[index] + + if not item.name.substr(0, _search_text.length()) == _search_text: + continue + + var node := _list_item.instantiate() as MaterialSelectPopupItem + _container.add_child(node) + + node.set_material_name(item.name) + + if item.material is ColorPaintMaterial: + var color_mat := item.material as ColorPaintMaterial + node.set_color(color_mat.color) + elif item.material is LinearGradientPaintMaterial: + var linear_gradient_mat := item.material as LinearGradientPaintMaterial + node.set_linear_gradient(linear_gradient_mat.gradient_texture) + elif item.material is RadialGradientPaintMaterial: + var radial_gradient_mat := item.material as RadialGradientPaintMaterial + node.set_radial_gradient(radial_gradient_mat.gradient_texture) + + node.on_clicked.connect(_on_clicked_item.bind(index)) + + +## 表示非表示が切り替わったときに呼び出されるコールバック。 +func _on_visibility_changed() -> void: + if visible: + _search_text = "" + _search.text = "" + _update_content() + + +## アイテムがクリックされたときに呼び出されるコールバック。 +func _on_clicked_item(index: int) -> void: + var material_id := Main.materials.list[index].id + on_material_selected.emit(material_id) + hide() + + +## 検索も列が変更されたときに呼び出されるコールバック。 +func _on_line_edit_text_changed(new_text: String) -> void: + _search_text = new_text + _update_content() diff --git a/godot/src/node/material_select_popup_item.gd b/godot/src/node/material_select_popup_item.gd new file mode 100644 index 0000000..c28ab11 --- /dev/null +++ b/godot/src/node/material_select_popup_item.gd @@ -0,0 +1,76 @@ +## マテリアル選択ポップアップのアイテムのノード。 +class_name MaterialSelectPopupItem +extends PanelContainer + + +@onready var _node: PanelContainer = $"." +@onready var _color_panel: Panel = %Color +@onready var _name_label: Label = %Label + +## この要素がクリックされたときに発火するシグナル。 +signal on_clicked() + +## マウスオーバーしているかどうか。 +var mouse_over: bool = false +## このマテリアルのマテリアルid。 +var material_id: String = "" + +## マテリアルの単一色の表示マテリアル。 +@export var _color_material: ShaderMaterial +## マテリアルの線形グラデーションの表示マテリアル。 +@export var _linear_gradient_material: ShaderMaterial +## マテリアルの放射グラデーションの表示マテリアル。 +@export var _radial_gradient_material: ShaderMaterial + + +func _ready() -> void: + _node.add_theme_stylebox_override("panel", _node.get_theme_stylebox("panel").duplicate() as StyleBox) + + +func _input(event: InputEvent) -> void: + if event is InputEventMouseButton: + var mouse_event := event as InputEventMouseButton + var node_mouse_position := _node.get_local_mouse_position() + var rect := Rect2(Vector2.ZERO, _node.size) + if rect.has_point(node_mouse_position) and mouse_event.button_index == MOUSE_BUTTON_LEFT and mouse_event.pressed: + on_clicked.emit() + + +## マテリアルの色を設定する。 +func set_color(color: Color) -> void: + var mat := _color_material.duplicate() as ShaderMaterial + mat.set_shader_parameter("fill_color", color) + _color_panel.material = mat + + +## マテリアルの線形グラデーションを設定する。 +func set_linear_gradient(gradient_texture: GradientTexture1D) -> void: + var mat := _linear_gradient_material.duplicate() as ShaderMaterial + mat.set_shader_parameter("gradient_texture", gradient_texture) + _color_panel.material = mat + + +## マテリアルの放射グラデーションを設定する。 +func set_radial_gradient(gradient_texture: GradientTexture1D) -> void: + var mat := _radial_gradient_material.duplicate() as ShaderMaterial + mat.set_shader_parameter("gradient_texture", gradient_texture) + _color_panel.material = mat + + +## マテリアルの名前を設定する。 +func set_material_name(new_name: String) -> void: + _name_label.text = new_name + + +## マウスオーバーしたときに呼び出されるコールバック。 +func _on_mouse_entered() -> void: + mouse_over = true + var style_box := _node.get_theme_stylebox("panel") as StyleBoxFlat + style_box.bg_color = Color(0.5, 0.8, 0.9, 0.25) + + +## マウスが外れたときに呼び出されるコールバック。 +func _on_mouse_exited() -> void: + mouse_over = false + var style_box := _node.get_theme_stylebox("panel") as StyleBoxFlat + style_box.bg_color = Color.TRANSPARENT diff --git a/godot/src/node/radial_gradient_picker.gd b/godot/src/node/radial_gradient_picker.gd new file mode 100644 index 0000000..78690a7 --- /dev/null +++ b/godot/src/node/radial_gradient_picker.gd @@ -0,0 +1,119 @@ +class_name RadialGradientPicker +extends Popup + + +@onready var _center_x: SpinBox = %CenterX +@onready var _center_y: SpinBox = %CenterY +@onready var _handle_1_x: SpinBox = %Handle1X +@onready var _handle_1_y: SpinBox = %Handle1Y +@onready var _handle_2_x: SpinBox = %Handle2X +@onready var _handle_2_y: SpinBox = %Handle2Y +@onready var _gradient_editor: GradientEditor = %GradientEditor + +## グラデーションが変更したときのシグナル。 +signal on_gradient_changed(gradient_material: RadialGradientPaintMaterial) + +## 現在編集中のマテリアル。 +var _gradient_material: RadialGradientPaintMaterial + + +func _ready() -> void: + _gradient_editor.on_gradient_changed.connect(_on_gradient_editor_on_gradient_changed) + + +## ピッカーを表示する。 +func show_picker(gradient_material: RadialGradientPaintMaterial) -> void: + _gradient_editor.set_gradient(gradient_material.gradient) + _center_x.set_value_no_signal(gradient_material.center_point.x) + _center_y.set_value_no_signal(gradient_material.center_point.y) + _handle_1_x.set_value_no_signal(gradient_material.handle_1_point.x) + _handle_1_y.set_value_no_signal(gradient_material.handle_1_point.y) + _handle_2_x.set_value_no_signal(gradient_material.handle_2_point.x) + _handle_2_y.set_value_no_signal(gradient_material.handle_2_point.y) + _gradient_material = gradient_material + show() + + +## グラデーションが変化したときのコールバック。 +func _on_gradient_editor_on_gradient_changed(gradient: Gradient) -> void: + _gradient_material.gradient = gradient + _gradient_material.gradient_texture.gradient = gradient + on_gradient_changed.emit(_gradient_material) + Main.on_change_material_parameters_changed.emit() + + +## ピッカーが非表示になったときに呼び出されるコールバック。 +func _on_popup_hide() -> void: + _gradient_editor.remove_color_arrow() + _gradient_material = null + + +## 中心点のXの値が変わったときのコールバック。 +func _on_center_x_value_changed(value: float) -> void: + var delta_x := value - _gradient_material.center_point.x + _gradient_material.center_point.x = value + _gradient_material.handle_1_point.x += delta_x + _gradient_material.handle_2_point.x += delta_x + _handle_1_x.set_value_no_signal(_gradient_material.handle_1_point.x) + _handle_2_x.set_value_no_signal(_gradient_material.handle_2_point.x) + Main.on_change_material_parameters_changed.emit() + + +## 中心点のYの値が変わったときのコールバック。 +func _on_center_y_value_changed(value: float) -> void: + var delta_y := value - _gradient_material.center_point.y + _gradient_material.center_point.y = value + _gradient_material.handle_1_point.y += delta_y + _gradient_material.handle_2_point.y += delta_y + _handle_1_y.set_value_no_signal(_gradient_material.handle_1_point.y) + _handle_2_y.set_value_no_signal(_gradient_material.handle_2_point.y) + Main.on_change_material_parameters_changed.emit() + + +## ハンドル1のXの値が変わったときのコールバック。 +func _on_handle_1x_value_changed(value: float) -> void: + _gradient_material.handle_1_point.x = value + var handle_2_length := (_gradient_material.handle_2_point - _gradient_material.center_point).length() + var handle_2_dir := (_gradient_material.handle_1_point - _gradient_material.center_point).normalized().rotated(-PI / 2) + _gradient_material.handle_2_point = _gradient_material.center_point + handle_2_dir * handle_2_length + _handle_2_x.set_value_no_signal(_gradient_material.handle_2_point.x) + _handle_2_y.set_value_no_signal(_gradient_material.handle_2_point.y) + Main.on_change_material_parameters_changed.emit() + + +## ハンドル1のYの値が変わったときのコールバック。 +func _on_handle_1y_value_changed(value: float) -> void: + _gradient_material.handle_1_point.y = value + var handle_2_length := (_gradient_material.handle_2_point - _gradient_material.center_point).length() + var handle_2_dir := (_gradient_material.handle_1_point - _gradient_material.center_point).normalized().rotated(-PI / 2) + _gradient_material.handle_2_point = _gradient_material.center_point + handle_2_dir * handle_2_length + _handle_2_x.set_value_no_signal(_gradient_material.handle_2_point.x) + _handle_2_y.set_value_no_signal(_gradient_material.handle_2_point.y) + Main.on_change_material_parameters_changed.emit() + + +## ハンドル2のXの値が変わったときのコールバック。 +func _on_handle_2x_value_changed(value: float) -> void: + _gradient_material.handle_2_point.x = value + var handle_1_length := (_gradient_material.handle_1_point - _gradient_material.center_point).length() + var handle_1_dir := (_gradient_material.handle_2_point - _gradient_material.center_point).normalized().rotated(PI / 2) + _gradient_material.handle_1_point = _gradient_material.center_point + handle_1_dir * handle_1_length + _handle_1_x.set_value_no_signal(_gradient_material.handle_1_point.x) + _handle_1_y.set_value_no_signal(_gradient_material.handle_1_point.y) + Main.on_change_material_parameters_changed.emit() + + +## ハンドル2のYの値が変わったときのコールバック。 +func _on_handle_2y_value_changed(value: float) -> void: + _gradient_material.handle_2_point.y = value + var handle_1_length := (_gradient_material.handle_1_point - _gradient_material.center_point).length() + var handle_1_dir := (_gradient_material.handle_2_point - _gradient_material.center_point).normalized().rotated(PI / 2) + _gradient_material.handle_1_point = _gradient_material.center_point + handle_1_dir * handle_1_length + _handle_1_x.set_value_no_signal(_gradient_material.handle_1_point.x) + _handle_1_y.set_value_no_signal(_gradient_material.handle_1_point.y) + Main.on_change_material_parameters_changed.emit() + + +## 編集ボタンが押されたときのコールバック。 +func _on_edit_button_pressed() -> void: + Main.emit_start_edit_radial_gradient(_gradient_material) diff --git a/godot/src/node/scroll_space.gd b/godot/src/node/scroll_space.gd new file mode 100644 index 0000000..d1a1ac1 --- /dev/null +++ b/godot/src/node/scroll_space.gd @@ -0,0 +1,84 @@ +## キャンバスタブ内部のdocumentのプレビューの平行移動や拡大縮小の挙動を記述するクラス。 +class_name ScrollSpace +extends Panel + + +@onready var _space: Panel = $"." +@onready var _document: Panel = $Document +@onready var _control_layer: Panel = $ControlLayer + +## documentのビューポートでの拡大率。 +var document_scale: float = 1.0 + +var _document_position_delta: Vector2 = Vector2() +var _middle_pressed: bool = false + +const WHEEL_SCALE_SPEED: float = 0.1 +const DOCUMENT_SCALE_MIN: float = 0.01 +const DOCUMENT_SCALE_MAX: float = 100 +const SPACE_SCALE: float = 2.8 + + +func _process(_delta: float) -> void: + _document.scale = Vector2(document_scale, document_scale) + _control_layer.scale = Vector2(document_scale, document_scale) + _document.position = _space.size / 2 - document_scale * _document.size / 2 \ + + _document_position_delta + _control_layer.position = _space.size / 2 - document_scale * _document.size / 2 \ + + _document_position_delta + + +func _input(event: InputEvent) -> void: + if not Main.document_opened: + return + + if event is InputEventMouseButton: + var mouse_button_event := event as InputEventMouseButton + var hover_space := Rect2(Vector2.ZERO, _space.size).has_point(_space.get_local_mouse_position()) + + # Wheelでdocument scaleを変更 + if mouse_button_event.button_index == MOUSE_BUTTON_WHEEL_DOWN and hover_space: + document_scale *= 1 - WHEEL_SCALE_SPEED + if document_scale < DOCUMENT_SCALE_MIN or document_scale > DOCUMENT_SCALE_MAX: + document_scale = clampf(document_scale, DOCUMENT_SCALE_MIN, DOCUMENT_SCALE_MAX) + else: + var mouse_pos := _space.get_local_mouse_position() - _space.size / 2 + _document_position_delta = \ + (_document_position_delta - mouse_pos) * (1 - WHEEL_SCALE_SPEED) + mouse_pos + Main.viewport_scale = document_scale + if mouse_button_event.button_index == MOUSE_BUTTON_WHEEL_UP and hover_space: + document_scale *= 1 + WHEEL_SCALE_SPEED + if document_scale < DOCUMENT_SCALE_MIN or document_scale > DOCUMENT_SCALE_MAX: + document_scale = clampf(document_scale, DOCUMENT_SCALE_MIN, DOCUMENT_SCALE_MAX) + else: + var mouse_pos := _space.get_local_mouse_position() - _space.size / 2 + _document_position_delta = \ + (_document_position_delta - mouse_pos) * (1 + WHEEL_SCALE_SPEED) + mouse_pos + Main.viewport_scale = document_scale + + # Middle Buttonがpressedでドキュメントの平行移動状態 + if mouse_button_event.button_index == MOUSE_BUTTON_MIDDLE: + if event.is_pressed() and hover_space: + _middle_pressed = true + else: + _middle_pressed = false + + if event is InputEventMouseMotion: + var mouse_motion_event := event as InputEventMouseMotion + if _middle_pressed: + _document_position_delta += mouse_motion_event.relative + + +## 画面内中央にdocumentを納めます。 +func expand_document() -> void: + _document_position_delta = Vector2() + var x_scale := _space.size.x / _document.size.x + var y_scale := _space.size.y / _document.size.y + document_scale = minf(minf(x_scale, y_scale), 1.0) + Main.viewport_scale = document_scale + + +## documentのサイズを100%にします。 +func reset_document_scale() -> void: + document_scale = 1.0 + Main.viewport_scale = document_scale diff --git a/godot/src/pool/paint_layer_pool.gd b/godot/src/pool/paint_layer_pool.gd new file mode 100644 index 0000000..a72606a --- /dev/null +++ b/godot/src/pool/paint_layer_pool.gd @@ -0,0 +1,81 @@ +class_name PaintLayerPool +extends RefCounted + + +const uuid_util = preload('res://addons/uuid/uuid.gd') + +## PathPaintLayerのプール +var _path_layer_pool: Array[PathPaintLayer] = [] +## FillPaintLayerのプール +var _fill_layer_pool: Array[FillPaintLayer] = [] +## GroupPaintLayerのプール +var _group_layer_pool: Array[GroupPaintLayer] = [] + + +## PathPaintLayerを取得する。 +func get_path_layer(new_layer_name: bool = true) -> PathPaintLayer: + if _path_layer_pool.size() > 1: + var path_layer: PathPaintLayer = _path_layer_pool.pop_back() + path_layer.fill_material_id = Main.materials.new_path_layer_fill_material_id + path_layer.outline_material_id = Main.materials.new_path_layer_line_material_id + if new_layer_name: + path_layer.layer_name = Main.layers.get_new_layer_name() + path_layer.id = uuid_util.v4() + return path_layer + return PathPaintLayer.new_layer() + + +## FillPaintLayerを取得する。 +func get_fill_layer(new_layer_name: bool = true) -> FillPaintLayer: + if _fill_layer_pool.size() > 1: + var fill_layer: FillPaintLayer = _fill_layer_pool.pop_back() + fill_layer.fill_material_id = Main.materials.new_fill_layer_material_id + if new_layer_name: + fill_layer.layer_name = Main.layers.get_new_layer_name() + fill_layer.id = uuid_util.v4() + return fill_layer + return FillPaintLayer.new_layer() + + +## GroupPaintLayerを取得する。 +func get_group_layer(new_layer_name: bool = true) -> GroupPaintLayer: + if _group_layer_pool.size() > 1: + var group_layer: GroupPaintLayer = _group_layer_pool.pop_back() + if new_layer_name: + group_layer.layer_name = Main.layers.get_new_layer_name() + group_layer.id = uuid_util.v4() + return group_layer + return GroupPaintLayer.new_layer() + + +## レイヤーをプールに返却する。 +func free_layer(layer: PaintLayer) -> void: + if layer is PathPaintLayer: + var path_layer := layer as PathPaintLayer + path_layer.clear_layer() + _path_layer_pool.push_back(path_layer) + elif layer is FillPaintLayer: + var fill_layer := layer as FillPaintLayer + fill_layer.clear_layer() + _fill_layer_pool.push_back(fill_layer) + elif layer is GroupPaintLayer: + var group_layer := layer as GroupPaintLayer + for l in group_layer.child_layers: + var already_free := false + if l is PathPaintLayer: + already_free = already_free or l in _path_layer_pool + if l is FillPaintLayer: + already_free = already_free or l in _fill_layer_pool + if l is GroupPaintLayer: + already_free = already_free or l in _group_layer_pool + if not already_free: + free_layer(l) + group_layer.clear_layer() + _group_layer_pool.push_back(group_layer) + + +## プールをクリアする。 +func clear() -> void: + _path_layer_pool.clear() + _fill_layer_pool.clear() + _group_layer_pool.clear() diff --git a/godot/src/pool/path_pool.gd b/godot/src/pool/path_pool.gd new file mode 100644 index 0000000..1b4055b --- /dev/null +++ b/godot/src/pool/path_pool.gd @@ -0,0 +1,30 @@ +class_name PathPool +extends RefCounted + + +const uuid_util = preload('res://addons/uuid/uuid.gd') + +## Pathのプール +var _path_pool: Array[Path] = [] + + +## PathPaintLayerを取得する。 +func get_path(new_path_name: bool = true) -> Path: + if _path_pool.size() > 1: + var path: Path = _path_pool.pop_back() + if new_path_name: + path.path_name = Main.layers.get_new_path_name() + path.id = uuid_util.v4() + return path + return Path.new_path() + + +## Pathをプールに返却する。 +func free_path(path: Path) -> void: + path.clear_path() + _path_pool.push_back(path) + + +## プールをクリアする。 +func clear() -> void: + _path_pool.clear() diff --git a/godot/src/serialize/document.gd b/godot/src/serialize/document.gd new file mode 100644 index 0000000..a37518e --- /dev/null +++ b/godot/src/serialize/document.gd @@ -0,0 +1,32 @@ +class_name Document +extends Resource + + +## レイヤーのリスト。 +@export var layers: Array[DocumentLayer] +## ドキュメントのサイズ。 +@export var document_size: Vector2i + +## マテリアルのリスト。 +@export var materials: Array[DocumentMaterial] +## 新規レイヤー作成時の塗りのマテリアルid。 +@export var new_path_layer_fill_material_id: String +## 新規レイヤー作成時の線のマテリアルid。 +@export var new_path_layer_line_material_id: String +## 新規レイヤー作成時の塗りのマテリアルid。 +@export var new_fill_layer_material_id: String + +## 現在選択されているレイヤーのidの配列。 +@export var selected_layers: Array[String] +## 現在選択されているパスのidの配列。 +@export var selected_paths: Array[String] + +## 最後に選択したPaintLayerのid。存在しない場合は空文字。 +@export var last_selected_layer: String +## 最後に選択したPathのid。存在しない場合は空文字。 +@export var last_selected_path: String +## 最後に選択したPaintLayerもしくはPathのid。存在しない場合は空文字。 +@export var last_selected_item: String + +## 現在描画先になっているPathPaintLayerのid。存在しない場合は空文字。 +@export var drawing_path_layer: String diff --git a/godot/src/serialize/document_layer.gd b/godot/src/serialize/document_layer.gd new file mode 100644 index 0000000..e95d7ad --- /dev/null +++ b/godot/src/serialize/document_layer.gd @@ -0,0 +1,12 @@ +class_name DocumentLayer +extends Resource + + +## レイヤーのブレンドモードの列挙子。 +enum BlendMode { + Normal, + Add, + Multiply, + Screen, + Overlay, +} diff --git a/godot/src/serialize/document_layer_fill.gd b/godot/src/serialize/document_layer_fill.gd new file mode 100644 index 0000000..3ad2421 --- /dev/null +++ b/godot/src/serialize/document_layer_fill.gd @@ -0,0 +1,22 @@ +class_name DocumentLayerFill +extends DocumentLayer + + +## レイヤーのid。 +@export var id: String + +## 下のレイヤーでクリップされているかどうか。 +@export var clipped: bool +## レイヤーが表示されているかどうか。 +@export var visible: bool +## レイヤーがロックされているかどうか。 +@export var locked: bool +## レイヤーのブレンドモード。 +@export var blend_mode: DocumentLayer.BlendMode +## alphaの%値 +@export var alpha: int +## レイヤーの名前。 +@export var layer_name: String + +## fillの色。 +@export var fill_material_id: String diff --git a/godot/src/serialize/document_layer_group.gd b/godot/src/serialize/document_layer_group.gd new file mode 100644 index 0000000..992feb9 --- /dev/null +++ b/godot/src/serialize/document_layer_group.gd @@ -0,0 +1,24 @@ +class_name DocumentLayerGroup +extends DocumentLayer + +## レイヤーのid。 +@export var id: String + +## 下のレイヤーでクリップされているかどうか。 +@export var clipped: bool +## レイヤーが表示されているかどうか。 +@export var visible: bool +## レイヤーがロックされているかどうか。 +@export var locked: bool +## レイヤーのブレンドモード。 +@export var blend_mode: DocumentLayer.BlendMode +## alphaの%値 +@export var alpha: int +## レイヤーの名前。 +@export var layer_name: String + +## 折りたたまれているか。 +@export var collapsed: bool + +## このグループレイヤーの子要素のレイヤー +@export var child_layers: Array[DocumentLayer] = [] diff --git a/godot/src/serialize/document_layer_path.gd b/godot/src/serialize/document_layer_path.gd new file mode 100644 index 0000000..6910e26 --- /dev/null +++ b/godot/src/serialize/document_layer_path.gd @@ -0,0 +1,34 @@ +class_name DocumentLayerPath +extends DocumentLayer + +## レイヤーのid。 +@export var id: String + +## 下のレイヤーでクリップされているかどうか。 +@export var clipped: bool +## レイヤーが表示されているかどうか。 +@export var visible: bool +## レイヤーがロックされているかどうか。 +@export var locked: bool +## レイヤーのブレンドモード。 +@export var blend_mode: DocumentLayer.BlendMode +## alphaの%値 +@export var alpha: int +## レイヤーの名前。 +@export var layer_name: String + +## fillするかどうか。 +@export var filled: bool +## fillの色。 +@export var fill_material_id: String +## outlineのオンオフ。 +@export var outlined: bool +## outlineの色。 +@export var outline_material_id: String +## outlineの幅。 +@export var outline_width: float +## 折りたたまれているか。 +@export var collapsed: bool + +## レイヤーの保持するパスの配列。 +@export var paths: Array[DocumentPath] diff --git a/godot/src/serialize/document_material.gd b/godot/src/serialize/document_material.gd new file mode 100644 index 0000000..216adfb --- /dev/null +++ b/godot/src/serialize/document_material.gd @@ -0,0 +1,8 @@ +class_name DocumentMaterial +extends Resource + + +## マテリアルID、 +@export var id: String +## マテリアルの名前。 +@export var name: String diff --git a/godot/src/serialize/document_material_color.gd b/godot/src/serialize/document_material_color.gd new file mode 100644 index 0000000..2b01289 --- /dev/null +++ b/godot/src/serialize/document_material_color.gd @@ -0,0 +1,6 @@ +class_name DocumentMaterialColor +extends DocumentMaterial + + +## マテリアルの色。 +@export var color: Color diff --git a/godot/src/serialize/document_material_linear_gradient.gd b/godot/src/serialize/document_material_linear_gradient.gd new file mode 100644 index 0000000..2083530 --- /dev/null +++ b/godot/src/serialize/document_material_linear_gradient.gd @@ -0,0 +1,13 @@ +class_name DocumentMaterialLinearGradient +extends DocumentMaterial + + +## マテリアルのグラデーションの色リスト。 +@export var colors: String +## マテリアルのグラデーションの位置リスト。 +@export var offsets: String + +## マテリアルのグラデーションの開始位置。 +@export var start_point: Vector2 +## マテリアルのグラデーションの終了位置。 +@export var end_point: Vector2 diff --git a/godot/src/serialize/document_material_radial_gradient.gd b/godot/src/serialize/document_material_radial_gradient.gd new file mode 100644 index 0000000..c6117af --- /dev/null +++ b/godot/src/serialize/document_material_radial_gradient.gd @@ -0,0 +1,15 @@ +class_name DocumentMaterialRadialGradient +extends DocumentMaterial + + +## マテリアルのグラデーションの色リスト。 +@export var colors: String +## マテリアルのグラデーションの位置リスト。 +@export var offsets: String + +## マテリアルのグラデーションの中心位置。 +@export var center_point: Vector2 +## マテリアルのグラデーションのハンドル1位置。 +@export var handle_1_point: Vector2 +## マテリアルのグラデーションのハンドル2位置。 +@export var handle_2_point: Vector2 diff --git a/godot/src/serialize/document_path.gd b/godot/src/serialize/document_path.gd new file mode 100644 index 0000000..2a7425e --- /dev/null +++ b/godot/src/serialize/document_path.gd @@ -0,0 +1,71 @@ +class_name DocumentPath +extends Resource + +## パスのブーリアンの列挙子 +enum Boolean { + Union, + Diff, + Intersect, + Xor, +} + + +## パスのid。 +@export var id: String + +## パスが表示されているかどうか。 +@export var visible: bool +## パスがロックされているかどうか。 +@export var locked: bool +## closed pathかどうか。 +@export var closed: bool +## パスのブーリアンの種類。 +@export var boolean: Boolean +## パスの名前。 +@export var path_name: String + +## ControlPointの配列。 +@export var control_points: String +## weightの配列。 +@export var weights: String +## phiの配列。 +@export var phis: String +## psiの配列。 +@export var psis: String +## パスのポリゴン形状のPackedVector2Array。 +@export var polygon: String +## polygonの頂点列のPackedVector2Array。 +@export var vertices: String +## polygonのindicesのPackedInt32Array。 +@export var indices: String +## polygonのthumbnail_indicesのPackedInt32Array。 +@export var thumbnail_indices: Array[String] +## polygonの頂点列のPackedVector2Array。 +@export var line_vertices: String +## polygonのindicesのPackedInt32Array。 +@export var line_indices: String +## segmentsのPackedVector2Arrayの配列。 +@export var segments: Array[String] + +## ControlPointの配列。 +var control_points_raw: PackedVector2Array = PackedVector2Array() +## weightの配列。 +var weights_raw: PackedFloat32Array = PackedFloat32Array() +## phiの配列。 +var phis_raw: PackedFloat32Array = PackedFloat32Array() +## psiの配列。 +var psis_raw: PackedFloat32Array = PackedFloat32Array() +## パスのポリゴン形状のPackedVector2Array。 +var polygon_raw: PackedVector2Array = PackedVector2Array() +## polygonの頂点列のPackedVector2Array。 +var vertices_raw: PackedVector2Array = PackedVector2Array() +## polygonのindicesのPackedInt32Array。 +var indices_raw: PackedInt32Array = PackedInt32Array() +## パスのサムネ用のindices +var thumbnail_indices_raw: Array[PackedInt32Array] = [] +## polygonの頂点列のPackedVector2Array。 +var line_vertices_raw: PackedVector2Array = PackedVector2Array() +## polygonのindicesのPackedInt32Array。 +var line_indices_raw: PackedInt32Array = PackedInt32Array() +## segmentsのPackedVector2Arrayの配列。 +var segments_raw: Array[PackedVector2Array] = [] diff --git a/godot/src/serialize/document_serializer.gd b/godot/src/serialize/document_serializer.gd new file mode 100644 index 0000000..9fa3e14 --- /dev/null +++ b/godot/src/serialize/document_serializer.gd @@ -0,0 +1,500 @@ +class_name DocumentSerializer +extends RefCounted + + +## パスレイヤーをdocumentエンコードする。 +static func _encode_path_layer(path_layer: PathPaintLayer, encode_base64: bool) -> DocumentLayerPath: + var document_layer_path := DocumentLayerPath.new() + document_layer_path.id = path_layer.id + document_layer_path.clipped = path_layer.clipped + document_layer_path.visible = path_layer.visible + document_layer_path.locked = path_layer.locked + match path_layer.blend_mode: + PaintLayer.BlendMode.Normal: + document_layer_path.blend_mode = DocumentLayer.BlendMode.Normal + PaintLayer.BlendMode.Add: + document_layer_path.blend_mode = DocumentLayer.BlendMode.Add + PaintLayer.BlendMode.Multiply: + document_layer_path.blend_mode = DocumentLayer.BlendMode.Multiply + PaintLayer.BlendMode.Screen: + document_layer_path.blend_mode = DocumentLayer.BlendMode.Screen + PaintLayer.BlendMode.Overlay: + document_layer_path.blend_mode = DocumentLayer.BlendMode.Overlay + document_layer_path.alpha = path_layer.alpha + document_layer_path.layer_name = path_layer.layer_name + + document_layer_path.filled = path_layer.filled + document_layer_path.fill_material_id = path_layer.fill_material_id + document_layer_path.outlined = path_layer.outlined + document_layer_path.outline_material_id = path_layer.outline_material_id + document_layer_path.outline_width = path_layer.outline_width + document_layer_path.collapsed = path_layer.collapsed + document_layer_path.paths = [] + for path in path_layer.paths: + var document_path := DocumentPath.new() + document_path.id = path.id + document_path.path_name = path.path_name + document_path.visible = path.visible + document_path.locked = path.locked + document_path.closed = path.closed + match path.boolean: + Path.Boolean.Union: + document_path.boolean = DocumentPath.Boolean.Union + Path.Boolean.Diff: + document_path.boolean = DocumentPath.Boolean.Diff + Path.Boolean.Intersect: + document_path.boolean = DocumentPath.Boolean.Intersect + Path.Boolean.Xor: + document_path.boolean = DocumentPath.Boolean.Xor + + if encode_base64: + document_path.control_points = Marshalls.raw_to_base64(var_to_bytes(path.control_points)) + document_path.weights = Marshalls.raw_to_base64(var_to_bytes(path.weights)) + document_path.phis = Marshalls.raw_to_base64(var_to_bytes(path.phis)) + document_path.psis = Marshalls.raw_to_base64(var_to_bytes(path.psis)) + document_path.polygon = Marshalls.raw_to_base64(var_to_bytes(path.polygon)) + document_path.vertices = Marshalls.raw_to_base64(var_to_bytes(path.vertices)) + document_path.indices = Marshalls.raw_to_base64(var_to_bytes(path.indices)) + document_path.thumbnail_indices = [] + for i in path.thumbnail_indices: + document_path.thumbnail_indices.append(Marshalls.raw_to_base64(var_to_bytes(i))) + document_path.line_vertices = Marshalls.raw_to_base64(var_to_bytes(path.line_vertices)) + document_path.line_indices = Marshalls.raw_to_base64(var_to_bytes(path.line_indices)) + document_path.segments = [] + for s in path.segments: + document_path.segments.append(Marshalls.raw_to_base64(var_to_bytes(s))) + else: + document_path.control_points_raw = path.control_points.duplicate() + document_path.weights_raw = path.weights.duplicate() + document_path.phis_raw = path.phis.duplicate() + document_path.psis_raw = path.psis.duplicate() + document_path.polygon_raw = path.polygon.duplicate() + document_path.vertices_raw = path.vertices.duplicate() + document_path.indices_raw = path.indices.duplicate() + document_path.thumbnail_indices_raw = [] + for i in path.thumbnail_indices: + document_path.thumbnail_indices_raw.append(i.duplicate()) + document_path.line_vertices_raw = path.line_vertices.duplicate() + document_path.line_indices_raw = path.line_indices.duplicate() + document_path.segments = [] + for s in path.segments: + document_path.segments_raw.append(s.duplicate()) + + document_layer_path.paths.append(document_path) + return document_layer_path + + +## 塗りつぶしレイヤーをdocumentにエンコードする。 +static func _encode_fill_layer(fill_layer: FillPaintLayer) -> DocumentLayerFill: + var document_layer_fill := DocumentLayerFill.new() + document_layer_fill.id = fill_layer.id + document_layer_fill.clipped = fill_layer.clipped + document_layer_fill.visible = fill_layer.visible + document_layer_fill.locked = fill_layer.locked + match fill_layer.blend_mode: + PaintLayer.BlendMode.Normal: + document_layer_fill.blend_mode = DocumentLayer.BlendMode.Normal + PaintLayer.BlendMode.Add: + document_layer_fill.blend_mode = DocumentLayer.BlendMode.Add + PaintLayer.BlendMode.Multiply: + document_layer_fill.blend_mode = DocumentLayer.BlendMode.Multiply + PaintLayer.BlendMode.Screen: + document_layer_fill.blend_mode = DocumentLayer.BlendMode.Screen + PaintLayer.BlendMode.Overlay: + document_layer_fill.blend_mode = DocumentLayer.BlendMode.Overlay + document_layer_fill.alpha = fill_layer.alpha + document_layer_fill.layer_name = fill_layer.layer_name + + document_layer_fill.fill_material_id = fill_layer.fill_material_id + return document_layer_fill + + +## グループレイヤーをdocumentにエンコードする。 +static func _encode_group_layer(group_layer: GroupPaintLayer, encode_base64: bool) -> DocumentLayerGroup: + var document_layer_group := DocumentLayerGroup.new() + document_layer_group.id = group_layer.id + document_layer_group.clipped = group_layer.clipped + document_layer_group.visible = group_layer.visible + document_layer_group.locked = group_layer.locked + match group_layer.blend_mode: + PaintLayer.BlendMode.Normal: + document_layer_group.blend_mode = DocumentLayer.BlendMode.Normal + PaintLayer.BlendMode.Add: + document_layer_group.blend_mode = DocumentLayer.BlendMode.Add + PaintLayer.BlendMode.Multiply: + document_layer_group.blend_mode = DocumentLayer.BlendMode.Multiply + PaintLayer.BlendMode.Screen: + document_layer_group.blend_mode = DocumentLayer.BlendMode.Screen + PaintLayer.BlendMode.Overlay: + document_layer_group.blend_mode = DocumentLayer.BlendMode.Overlay + document_layer_group.alpha = group_layer.alpha + document_layer_group.layer_name = group_layer.layer_name + + document_layer_group.collapsed = group_layer.collapsed + document_layer_group.child_layers = [] + for layer in group_layer.child_layers: + if layer is PathPaintLayer: + var pl := layer as PathPaintLayer + document_layer_group.child_layers.append(_encode_path_layer(pl, encode_base64)) + if layer is FillPaintLayer: + var fl := layer as FillPaintLayer + document_layer_group.child_layers.append(_encode_fill_layer(fl)) + if layer is GroupPaintLayer: + var gl := layer as GroupPaintLayer + document_layer_group.child_layers.append(_encode_group_layer(gl, encode_base64)) + + return document_layer_group + + +## Mainをdocumentにストアする。 +static func document_store(encode_base64: bool) -> Document: + var document := Document.new() + + document.document_size = Main.document_size + + # マテリアルをエンコードする + var document_materials: Array[DocumentMaterial] = [] + for item in Main.materials.list: + if item.material is ColorPaintMaterial: + var color_mat := item.material as ColorPaintMaterial + var document_material_color := DocumentMaterialColor.new() + document_material_color.id = item.id + document_material_color.name = item.name + document_material_color.color = color_mat.color + document_materials.append(document_material_color) + elif item.material is LinearGradientPaintMaterial: + var linear_mat := item.material as LinearGradientPaintMaterial + var document_material_linear := DocumentMaterialLinearGradient.new() + document_material_linear.id = item.id + document_material_linear.name = item.name + document_material_linear.colors = Marshalls.raw_to_base64(var_to_bytes(linear_mat.gradient.colors)) + document_material_linear.offsets = Marshalls.raw_to_base64(var_to_bytes(linear_mat.gradient.offsets)) + document_material_linear.start_point = linear_mat.start_point + document_material_linear.end_point = linear_mat.end_point + document_materials.append(document_material_linear) + elif item.material is RadialGradientPaintMaterial: + var radial_mat := item.material as RadialGradientPaintMaterial + var document_material_radial := DocumentMaterialRadialGradient.new() + document_material_radial.id = item.id + document_material_radial.name = item.name + document_material_radial.colors = Marshalls.raw_to_base64(var_to_bytes(radial_mat.gradient.colors)) + document_material_radial.offsets = Marshalls.raw_to_base64(var_to_bytes(radial_mat.gradient.offsets)) + document_material_radial.center_point = radial_mat.center_point + document_material_radial.handle_1_point = radial_mat.handle_1_point + document_material_radial.handle_2_point = radial_mat.handle_2_point + document_materials.append(document_material_radial) + document.materials = document_materials + document.new_fill_layer_material_id = Main.materials.new_fill_layer_material_id + document.new_path_layer_fill_material_id = Main.materials.new_path_layer_fill_material_id + document.new_path_layer_line_material_id = Main.materials.new_path_layer_line_material_id + + # レイヤーをエンコードする + var document_layers: Array[DocumentLayer] = [] + for layer in Main.layers.root: + if layer is PathPaintLayer: + var path_layer := layer as PathPaintLayer + document_layers.append(_encode_path_layer(path_layer, encode_base64)) + if layer is FillPaintLayer: + var fill_layer := layer as FillPaintLayer + document_layers.append(_encode_fill_layer(fill_layer)) + if layer is GroupPaintLayer: + var group_layer := layer as GroupPaintLayer + document_layers.append(_encode_group_layer(group_layer, encode_base64)) + document.layers = document_layers + + # 選択中のレイヤーなどを保存する。 + document.selected_layers = [] + for layer in Main.layers.selected_layers: + document.selected_layers.append(layer.id) + document.selected_paths = [] + for path in Main.layers.selected_paths: + document.selected_paths.append(path.id) + if Main.layers.last_selected_layer != null: + document.last_selected_layer = Main.layers.last_selected_layer.id + if Main.layers.last_selected_path != null: + document.last_selected_path = Main.layers.last_selected_path.id + if Main.layers.last_selected_item != null: + document.last_selected_item = Main.layers.last_selected_item.id + if Main.layers.drawing_path_layer != null: + document.drawing_path_layer = Main.layers.drawing_path_layer.id + + return document + + +## パスレイヤーをdocumentデコードする。 +static func _decode_path_layer(document_layer_path: DocumentLayerPath, decode_base64: bool) -> PathPaintLayer: + var path_layer := Main.layer_pool.get_path_layer(false) + if not document_layer_path.id.is_empty(): + path_layer.id = document_layer_path.id + path_layer.clipped = document_layer_path.clipped + path_layer.visible = document_layer_path.visible + path_layer.locked = document_layer_path.locked + match document_layer_path.blend_mode: + DocumentLayer.BlendMode.Normal: + path_layer.blend_mode = PaintLayer.BlendMode.Normal + DocumentLayer.BlendMode.Add: + path_layer.blend_mode = PaintLayer.BlendMode.Add + DocumentLayer.BlendMode.Multiply: + path_layer.blend_mode = PaintLayer.BlendMode.Multiply + DocumentLayer.BlendMode.Screen: + path_layer.blend_mode = PaintLayer.BlendMode.Screen + DocumentLayer.BlendMode.Overlay: + path_layer.blend_mode = PaintLayer.BlendMode.Overlay + path_layer.alpha = document_layer_path.alpha + path_layer.layer_name = document_layer_path.layer_name + + path_layer.filled = document_layer_path.filled + path_layer.fill_material_id = document_layer_path.fill_material_id + path_layer.outlined = document_layer_path.outlined + path_layer.outline_material_id = document_layer_path.outline_material_id + path_layer.outline_width = document_layer_path.outline_width + path_layer.collapsed = document_layer_path.collapsed + for document_path in document_layer_path.paths: + var path := path_layer.add_path(false) + if not document_path.id.is_empty(): + path.id = document_path.id + path.path_name = document_path.path_name + path.visible = document_path.visible + path.locked = document_path.locked + path.closed = document_path.closed + match document_path.boolean: + DocumentPath.Boolean.Union: + path.boolean = Path.Boolean.Union + DocumentPath.Boolean.Diff: + path.boolean = Path.Boolean.Diff + DocumentPath.Boolean.Intersect: + path.boolean = Path.Boolean.Intersect + DocumentPath.Boolean.Xor: + path.boolean = Path.Boolean.Xor + + if decode_base64: + var need_recalculate := false + @warning_ignore("unsafe_cast") + path.control_points = bytes_to_var(Marshalls.base64_to_raw(document_path.control_points)) as PackedVector2Array + @warning_ignore("unsafe_cast") + path.weights = bytes_to_var(Marshalls.base64_to_raw(document_path.weights)) as PackedFloat32Array + @warning_ignore("unsafe_cast") + path.phis = bytes_to_var(Marshalls.base64_to_raw(document_path.phis)) as PackedFloat32Array + @warning_ignore("unsafe_cast") + path.psis = bytes_to_var(Marshalls.base64_to_raw(document_path.psis)) as PackedFloat32Array + if document_path.polygon.is_empty(): + need_recalculate = true + else: + @warning_ignore("unsafe_cast") + path.polygon = bytes_to_var(Marshalls.base64_to_raw(document_path.polygon)) as PackedVector2Array + if document_path.vertices.is_empty(): + need_recalculate = true + else: + @warning_ignore("unsafe_cast") + path.vertices = bytes_to_var(Marshalls.base64_to_raw(document_path.vertices)) as PackedVector2Array + if document_path.indices.is_empty(): + need_recalculate = true + else: + @warning_ignore("unsafe_cast") + path.indices = bytes_to_var(Marshalls.base64_to_raw(document_path.indices)) as PackedInt32Array + if document_path.thumbnail_indices.size() == 0: + need_recalculate = true + else: + path.thumbnail_indices = [] + for i in document_path.thumbnail_indices: + @warning_ignore("unsafe_cast") + path.thumbnail_indices.append(bytes_to_var(Marshalls.base64_to_raw(i)) as PackedInt32Array) + if document_path.line_vertices.is_empty(): + need_recalculate = true + else: + @warning_ignore("unsafe_cast") + path.line_vertices = bytes_to_var(Marshalls.base64_to_raw(document_path.line_vertices)) as PackedVector2Array + if document_path.line_indices.is_empty(): + need_recalculate = true + else: + @warning_ignore("unsafe_cast") + path.line_indices = bytes_to_var(Marshalls.base64_to_raw(document_path.line_indices)) as PackedInt32Array + if document_path.segments.size() == 0: + need_recalculate = true + else: + path.segments = [] + for s in document_path.segments: + @warning_ignore("unsafe_cast") + path.segments.append(bytes_to_var(Marshalls.base64_to_raw(s)) as PackedVector2Array) + if need_recalculate: + path.recalculate_polygon() + else: + path.control_points = document_path.control_points_raw.duplicate() + path.weights = document_path.weights_raw.duplicate() + path.phis = document_path.phis_raw.duplicate() + path.psis = document_path.psis_raw.duplicate() + path.polygon = document_path.polygon_raw.duplicate() + path.vertices = document_path.vertices_raw.duplicate() + path.indices = document_path.indices_raw.duplicate() + path.thumbnail_indices = [] + for i in document_path.thumbnail_indices_raw: + path.thumbnail_indices.append(i.duplicate()) + path.line_vertices = document_path.line_vertices_raw.duplicate() + path.line_indices = document_path.line_indices_raw.duplicate() + path.segments = [] + for s in document_path.segments_raw: + path.segments.append(s.duplicate()) + + path_layer.need_composite = true + path_layer.update_child_paths_thumbnail() + + return path_layer + + +## 塗りつぶしレイヤーをdocumentにデコードする。 +static func _decode_fill_layer(document_layer_fill: DocumentLayerFill) -> FillPaintLayer: + var fill_layer := Main.layer_pool.get_fill_layer(false) + if not document_layer_fill.id.is_empty(): + fill_layer.id = document_layer_fill.id + fill_layer.clipped = document_layer_fill.clipped + fill_layer.visible = document_layer_fill.visible + fill_layer.locked = document_layer_fill.locked + match document_layer_fill.blend_mode: + DocumentLayer.BlendMode.Normal: + fill_layer.blend_mode = PaintLayer.BlendMode.Normal + DocumentLayer.BlendMode.Add: + fill_layer.blend_mode = PaintLayer.BlendMode.Add + DocumentLayer.BlendMode.Multiply: + fill_layer.blend_mode = PaintLayer.BlendMode.Multiply + DocumentLayer.BlendMode.Screen: + fill_layer.blend_mode = PaintLayer.BlendMode.Screen + DocumentLayer.BlendMode.Overlay: + fill_layer.blend_mode = PaintLayer.BlendMode.Overlay + fill_layer.alpha = document_layer_fill.alpha + fill_layer.layer_name = document_layer_fill.layer_name + + fill_layer.need_composite = true + + fill_layer.fill_material_id = document_layer_fill.fill_material_id + return fill_layer + + +## グループレイヤーをdocumentにデコードする。 +static func _decode_group_layer(document_layer_group: DocumentLayerGroup, decode_base64: bool) -> GroupPaintLayer: + var group_layer := Main.layer_pool.get_group_layer(false) + if not document_layer_group.id.is_empty(): + group_layer.id = document_layer_group.id + group_layer.clipped = document_layer_group.clipped + group_layer.visible = document_layer_group.visible + group_layer.locked = document_layer_group.locked + match document_layer_group.blend_mode: + DocumentLayer.BlendMode.Normal: + group_layer.blend_mode = PaintLayer.BlendMode.Normal + DocumentLayer.BlendMode.Add: + group_layer.blend_mode = PaintLayer.BlendMode.Add + DocumentLayer.BlendMode.Multiply: + group_layer.blend_mode = PaintLayer.BlendMode.Multiply + DocumentLayer.BlendMode.Screen: + group_layer.blend_mode = PaintLayer.BlendMode.Screen + DocumentLayer.BlendMode.Overlay: + group_layer.blend_mode = PaintLayer.BlendMode.Overlay + group_layer.alpha = document_layer_group.alpha + group_layer.layer_name = document_layer_group.layer_name + + group_layer.collapsed = document_layer_group.collapsed + group_layer.child_layers = [] + for layer in document_layer_group.child_layers: + if layer is DocumentLayerPath: + var pl := layer as DocumentLayerPath + group_layer.child_layers.append(_decode_path_layer(pl, decode_base64)) + if layer is DocumentLayerFill: + var fl := layer as DocumentLayerFill + group_layer.child_layers.append(_decode_fill_layer(fl)) + if layer is DocumentLayerGroup: + var gl := layer as DocumentLayerGroup + group_layer.child_layers.append(_decode_group_layer(gl, decode_base64)) + + group_layer.need_composite = true + + return group_layer + +## Mainにdocumentをロードする。 +static func document_load(document: Document, decode_base64: bool) -> void: + # 現在の状態をクリアする + Main.materials.clear() + Main.layers.clear() + + Main.document_size = document.document_size + + Main.compositor.resize(Main.document_size) + + # マテリアルを読み込む。 + for document_material in document.materials: + if document_material is DocumentMaterialColor: + var document_material_color := document_material as DocumentMaterialColor + var material := ColorPaintMaterial.new() + material.color = document_material_color.color + var item := MaterialList.PaintMaterialListItem.new() + item.id = document_material_color.id + item.name = document_material_color.name + item.material = material + Main.materials.list.append(item) + elif document_material is DocumentMaterialLinearGradient: + var document_material_linear := document_material as DocumentMaterialLinearGradient + var material := LinearGradientPaintMaterial.new() + @warning_ignore("unsafe_cast") + material.gradient.colors = bytes_to_var(Marshalls.base64_to_raw(document_material_linear.colors)) as PackedColorArray + @warning_ignore("unsafe_cast") + material.gradient.offsets = bytes_to_var(Marshalls.base64_to_raw(document_material_linear.offsets)) as PackedFloat32Array + material.start_point = document_material_linear.start_point + material.end_point = document_material_linear.end_point + var item := MaterialList.PaintMaterialListItem.new() + item.id = document_material_linear.id + item.name = document_material_linear.name + item.material = material + Main.materials.list.append(item) + elif document_material is DocumentMaterialRadialGradient: + var document_material_radial := document_material as DocumentMaterialRadialGradient + var material := RadialGradientPaintMaterial.new() + @warning_ignore("unsafe_cast") + material.gradient.colors = bytes_to_var(Marshalls.base64_to_raw(document_material_radial.colors)) as PackedColorArray + @warning_ignore("unsafe_cast") + material.gradient.offsets = bytes_to_var(Marshalls.base64_to_raw(document_material_radial.offsets)) as PackedFloat32Array + material.center_point = document_material_radial.center_point + material.handle_1_point = document_material_radial.handle_1_point + material.handle_2_point = document_material_radial.handle_2_point + var item := MaterialList.PaintMaterialListItem.new() + item.id = document_material_radial.id + item.name = document_material_radial.name + item.material = material + Main.materials.list.append(item) + Main.materials.new_fill_layer_material_id = document.new_fill_layer_material_id + Main.materials.new_path_layer_fill_material_id = document.new_path_layer_fill_material_id + Main.materials.new_path_layer_line_material_id = document.new_path_layer_line_material_id + + # レイヤーを読み込む。 + for layer in document.layers: + if layer is DocumentLayerPath: + var path_layer := layer as DocumentLayerPath + Main.layers.root.append(_decode_path_layer(path_layer, decode_base64)) + if layer is DocumentLayerFill: + var fill_layer := layer as DocumentLayerFill + Main.layers.root.append(_decode_fill_layer(fill_layer)) + if layer is DocumentLayerGroup: + var group_layer := layer as DocumentLayerGroup + Main.layers.root.append(_decode_group_layer(group_layer, decode_base64)) + + # 選択中のレイヤーなどを復元する。 + for layer_id in document.selected_layers: + var layer := Main.layers.get_layer_by_id(layer_id) + if layer: + Main.layers.selected_layers.append(layer) + for path_id in document.selected_paths: + var path := Main.layers.get_path_by_id(path_id) + if path: + Main.layers.selected_paths.append(path) + if not document.last_selected_layer.is_empty(): + Main.layers.last_selected_layer = Main.layers.get_layer_by_id(document.last_selected_layer) + if not document.last_selected_path.is_empty(): + Main.layers.last_selected_path = Main.layers.get_path_by_id(document.last_selected_path) + if not document.last_selected_item.is_empty(): + if Main.layers.get_layer_by_id(document.last_selected_item): + Main.layers.last_selected_item = Main.layers.get_layer_by_id(document.last_selected_item) + else: + Main.layers.last_selected_item = Main.layers.get_path_by_id(document.last_selected_item) + if not document.drawing_path_layer.is_empty(): + Main.layers.drawing_path_layer = Main.layers.get_layer_by_id(document.drawing_path_layer) + + Main.compositor.update() + + Main.document_opened = true diff --git a/godot/src/sub_window/config_window.gd b/godot/src/sub_window/config_window.gd new file mode 100644 index 0000000..189da91 --- /dev/null +++ b/godot/src/sub_window/config_window.gd @@ -0,0 +1,73 @@ +class_name ConfigWindow +extends Window + + +@onready var _language_menu_button: MenuButton = %LanguageMenuButton +@onready var _auto_save_check_box: CheckBox = %AutoSaveCheckBox +@onready var _auto_save_spin_box: SpinBox = %AutoSaveSpinBox + + +func _ready() -> void: + _language_menu_button.get_popup().id_pressed.connect(_on_language_menu_button_id_pressed) + +## 設定値を表示する。 +func show_config() -> void: + var config := ConfigFile.new() + + var ret := config.load("user://config.cfg") + if ret == OK: + if config.has_section_key("auto_save", "enabled"): + _auto_save_check_box.button_pressed = config.get_value("auto_save", "enabled") + if config.has_section_key("auto_save", "interval"): + _auto_save_spin_box.value = config.get_value("auto_save", "interval") + + show() + + +## 言語が変更されたときの処理。 +func _on_language_menu_button_id_pressed(id: int) -> void: + var config := ConfigFile.new() + config.load("user://config.cfg") + match id: + 0: + TranslationServer.set_locale("ja") + config.set_value("language", "locale", "ja") + 1: + TranslationServer.set_locale("en") + config.set_value("language", "locale", "en") + config.save("user://config.cfg") + Main.emit_locale_changed() + + +## オートセーブのチェックボックスをトグルしたときの処理。 +func _on_auto_save_check_box_toggled(toggled_on: bool) -> void: + var config := ConfigFile.new() + + config.load("user://config.cfg") + config.set_value("auto_save", "enabled", toggled_on) + config.save("user://config.cfg") + + _auto_save_check_box.button_pressed = toggled_on + Main.auto_save_enabled = toggled_on + + +## オートセーブの間隔を変更したときの処理。 +func _on_auto_save_spin_box_value_changed(value: float) -> void: + var config := ConfigFile.new() + + config.load("user://config.cfg") + config.set_value("auto_save", "interval", int(value)) + config.save("user://config.cfg") + + _auto_save_spin_box.value = int(value) + Main.auto_save_interval = int(value) + + +## ウィンドウが閉じられるときの処理。 +func _on_close_requested() -> void: + hide() + + +## 閉じるボタンが押されたときの処理。 +func _on_close_button_pressed() -> void: + hide() diff --git a/godot/src/sub_window/document_export_window.gd b/godot/src/sub_window/document_export_window.gd new file mode 100644 index 0000000..0a5307f --- /dev/null +++ b/godot/src/sub_window/document_export_window.gd @@ -0,0 +1,45 @@ +class_name DocumentExportWindow +extends Window + + +## ドキュメントの作成が押されたときのシグナル。 +signal on_export_document(document_size: Vector2) + +@onready var _width_spin_box: SpinBox = %WidthSpinBox +@onready var _height_spin_box: SpinBox = %HeightSpinBox + + +## 表示するドキュメントサイズを変える。 +func set_document_size(document_size: Vector2) -> void: + _width_spin_box.value = document_size.x + _height_spin_box.value = document_size.y + + +## 作成ボタンが押された際のコールバック。 +func _on_create_button_pressed() -> void: + hide() + on_export_document.emit(Vector2(_width_spin_box.value, _height_spin_box.value)) + + +## キャンセルボタンが押された際のコールバック。 +func _on_cancel_button_pressed() -> void: + hide() + + +## windowのcloseがリクエストされた際のコールバック。 +func _on_close_requested() -> void: + hide() + + +## 幅の値が変更された際のコールバック。 +func _on_width_spin_box_value_changed(value: float) -> void: + var new_width := roundi(value) + var new_height := int(Main.document_size.y * new_width / Main.document_size.x) + _height_spin_box.set_value_no_signal(new_height) + + +## 高さの値が変更された際のコールバック。 +func _on_height_spin_box_value_changed(value: float) -> void: + var new_height := roundi(value) + var new_width := int(Main.document_size.x * new_height / Main.document_size.y) + _width_spin_box.set_value_no_signal(new_width) diff --git a/godot/src/sub_window/document_open_window.gd b/godot/src/sub_window/document_open_window.gd new file mode 100644 index 0000000..c0a55c1 --- /dev/null +++ b/godot/src/sub_window/document_open_window.gd @@ -0,0 +1,72 @@ +class_name DocumentOpenWindow +extends Window + + +## ドキュメントの作成が押されたときのシグナル。 +signal on_create_document(document_size: Vector2) + +@onready var _preset_menu: MenuButton = %MenuButton +@onready var _width_spin_box: SpinBox = %WidthSpinBox +@onready var _height_spin_box: SpinBox = %HeightSpinBox + + +func _ready() -> void: + _preset_menu.get_popup().id_pressed.connect(_on_preset) + + +## 表示するドキュメントサイズを変える。 +func set_document_size(document_size: Vector2) -> void: + _width_spin_box.value = document_size.x + _height_spin_box.value = document_size.y + + +## プリセットの選択。 +func _on_preset(id: int) -> void: + match id: + 0: + _width_spin_box.value = 800 + _height_spin_box.value = 600 + 1: + _width_spin_box.value = 1024 + _height_spin_box.value = 600 + 2: + _width_spin_box.value = 1024 + _height_spin_box.value = 768 + 3: + _width_spin_box.value = 1280 + _height_spin_box.value = 720 + 4: + _width_spin_box.value = 1280 + _height_spin_box.value = 960 + 5: + _width_spin_box.value = 1600 + _height_spin_box.value = 900 + 6: + _width_spin_box.value = 1600 + _height_spin_box.value = 1200 + 7: + _width_spin_box.value = 1920 + _height_spin_box.value = 1080 + + +## 作成ボタンが押された際のコールバック。 +func _on_create_button_pressed() -> void: + hide() + on_create_document.emit(Vector2(_width_spin_box.value, _height_spin_box.value)) + + +## キャンセルボタンが押された際のコールバック。 +func _on_cancel_button_pressed() -> void: + hide() + + +## windowのcloseがリクエストされた際のコールバック。 +func _on_close_requested() -> void: + hide() + + +## サイズの縦横入れ替えのコールバック。 +func _on_switch_button_pressed() -> void: + var prev_size := Vector2(_width_spin_box.value, _height_spin_box.value) + _width_spin_box.value = prev_size.y + _height_spin_box.value = prev_size.x diff --git a/godot/src/sub_window/document_size_change_window.gd b/godot/src/sub_window/document_size_change_window.gd new file mode 100644 index 0000000..0af6eee --- /dev/null +++ b/godot/src/sub_window/document_size_change_window.gd @@ -0,0 +1,130 @@ +class_name DocumentSizeChangeWindow +extends Window + + +## ドキュメントのサイズ変更が押されたときのシグナル。 +signal on_change_document_size(document_size: Vector2, anchor: PaintLayer.ScaleAnchor) + +@onready var _width_spin_box: SpinBox = %WidthSpinBox +@onready var _height_spin_box: SpinBox = %HeightSpinBox +@onready var _top_left_rect: ColorRect = %TopLeftButton/ColorRect +@onready var _top_rect: ColorRect = %TopButton/ColorRect +@onready var _top_right_rect: ColorRect = %TopRightButton/ColorRect +@onready var _left_rect: ColorRect = %LeftButton/ColorRect +@onready var _center_rect: ColorRect = %CenterButton/ColorRect +@onready var _right_rect: ColorRect = %RightButton/ColorRect +@onready var _bottom_left_rect: ColorRect = %BottomLeftButton/ColorRect +@onready var _bottom_rect: ColorRect = %BottomButton/ColorRect +@onready var _bottom_right_rect: ColorRect = %BottomRightButton/ColorRect + + +var _anchor: PaintLayer.ScaleAnchor = PaintLayer.ScaleAnchor.Center + + +## 表示するドキュメントサイズとアンカーを変える。 +func set_document_size_and_anchor(document_size: Vector2, anchor: PaintLayer.ScaleAnchor) -> void: + _width_spin_box.value = document_size.x + _height_spin_box.value = document_size.y + _update_anchor(anchor) + + +## アンカーボタンを見た目に反映する。 +func _update_anchor(anchor: PaintLayer.ScaleAnchor) -> void: + _anchor = anchor + + _top_left_rect.color = Color.GRAY + _top_rect.color = Color.GRAY + _top_right_rect.color = Color.GRAY + _left_rect.color = Color.GRAY + _center_rect.color = Color.GRAY + _right_rect.color = Color.GRAY + _bottom_left_rect.color = Color.GRAY + _bottom_rect.color = Color.GRAY + _bottom_right_rect.color = Color.GRAY + match anchor: + PaintLayer.ScaleAnchor.TopLeft: + _top_left_rect.color = Color.WHITE + PaintLayer.ScaleAnchor.TopCenter: + _top_rect.color = Color.WHITE + PaintLayer.ScaleAnchor.TopRight: + _top_right_rect.color = Color.WHITE + PaintLayer.ScaleAnchor.Left: + _left_rect.color = Color.WHITE + PaintLayer.ScaleAnchor.Center: + _center_rect.color = Color.WHITE + PaintLayer.ScaleAnchor.Right: + _right_rect.color = Color.WHITE + PaintLayer.ScaleAnchor.BottomLeft: + _bottom_left_rect.color = Color.WHITE + PaintLayer.ScaleAnchor.BottomCenter: + _bottom_rect.color = Color.WHITE + PaintLayer.ScaleAnchor.BottomRight: + _bottom_right_rect.color = Color.WHITE + + +## サイズ変更ボタンが押された際のコールバック。 +func _on_create_button_pressed() -> void: + hide() + on_change_document_size.emit(Vector2(_width_spin_box.value, _height_spin_box.value), _anchor) + + +## キャンセルボタンが押された際のコールバック。 +func _on_cancel_button_pressed() -> void: + hide() + + +## windowのcloseがリクエストされた際のコールバック。 +func _on_close_requested() -> void: + hide() + + +## サイズの縦横入れ替えのコールバック。 +func _on_switch_button_pressed() -> void: + var prev_size := Vector2(_width_spin_box.value, _height_spin_box.value) + _width_spin_box.value = prev_size.y + _height_spin_box.value = prev_size.x + + +## 左上のアンカーボタンが押された際のコールバック。 +func _on_top_left_button_pressed() -> void: + _update_anchor(PaintLayer.ScaleAnchor.TopLeft) + + +## 上のアンカーボタンが押された際のコールバック。 +func _on_top_button_pressed() -> void: + _update_anchor(PaintLayer.ScaleAnchor.TopCenter) + + +## 右上のアンカーボタンが押された際のコールバック。 +func _on_top_right_button_pressed() -> void: + _update_anchor(PaintLayer.ScaleAnchor.TopRight) + + +## 左のアンカーボタンが押された際のコールバック。 +func _on_left_button_pressed() -> void: + _update_anchor(PaintLayer.ScaleAnchor.Left) + + +## 中央のアンカーボタンが押された際のコールバック。 +func _on_center_button_pressed() -> void: + _update_anchor(PaintLayer.ScaleAnchor.Center) + + +## 右のアンカーボタンが押された際のコールバック。 +func _on_right_button_pressed() -> void: + _update_anchor(PaintLayer.ScaleAnchor.Right) + + +## 左下のアンカーボタンが押された際のコールバック。 +func _on_bottom_left_button_pressed() -> void: + _update_anchor(PaintLayer.ScaleAnchor.BottomLeft) + + +## 下のアンカーボタンが押された際のコールバック。 +func _on_bottom_button_pressed() -> void: + _update_anchor(PaintLayer.ScaleAnchor.BottomCenter) + + +## 右下のアンカーボタンが押された際のコールバック。 +func _on_bottom_right_button_pressed() -> void: + _update_anchor(PaintLayer.ScaleAnchor.BottomRight) diff --git a/godot/src/sub_window/licenses_window.gd b/godot/src/sub_window/licenses_window.gd new file mode 100644 index 0000000..22f7590 --- /dev/null +++ b/godot/src/sub_window/licenses_window.gd @@ -0,0 +1,12 @@ +class_name LicensesWindow +extends Window + + +## ウィンドウの閉じるリクエストがあったときの処理。 +func _on_close_requested() -> void: + hide() + + +## 閉じるボタンが押されたときの処理。 +func _on_button_pressed() -> void: + hide() diff --git a/godot/src/tab/canvas_tab.gd b/godot/src/tab/canvas_tab.gd new file mode 100644 index 0000000..21405c6 --- /dev/null +++ b/godot/src/tab/canvas_tab.gd @@ -0,0 +1,705 @@ +## キャンバスタブの挙動を記述するクラス。 +extends PanelContainer +class_name CanvasTab + + +@onready var _space: ScrollSpace = %ScrollSpace +@onready var _document: Panel = %ScrollSpace/Document +@onready var _control_layer: Panel = %ScrollSpace/ControlLayer +@onready var _control_transform: ControlTransform = %ControlTransform +@onready var _control_linear_gradinet: ControlLinearGradient = %ControlLinearGradient +@onready var _control_radial_gradinet: ControlRadialGradient = %ControlRadialGradient +@onready var _drag_area_panel: Panel = %ScrollSpace/DragAreaPanel +@onready var _drawing_tool_highlight: Sprite2D = %DrawingButton/ToolHighlight +@onready var _manipulate_tool_highlight: Sprite2D = %ManipulateButton/ToolHighlight +@onready var _parameter_tuning_tool_highlight: Sprite2D = %ParameterTuningButton/ToolHighlight +@onready var _mirror_sprite: Sprite2D = %MirrorSprite +@onready var _mirror_flip_sprite: Sprite2D = %MirrorFlipSprite +@onready var _parameter_panel: Panel = %ParameterPanel +@onready var _parameter_label1: Label = %ParameterLabel1 +@onready var _parameter_label2: Label = %ParameterLabel2 + +## ツールの種類の列挙子。 +enum Tool { + DRAWING, + MANIPULATE, + PARAMETER_TUNING, + LINEAR_GRADIENT_EDITING, + RADIAL_GRADIENT_EDITING, +} +## 現在のツール。 +var _current_tool: Tool = Tool.DRAWING + +## 現在表示しているControlPointの配列。 +var _control_points: Array[ControlPoint] = [] +## 現在表示しているControlSegmentの配列。 +var _control_segments: Array[ControlSegment] = [] + +## 現在描画中のPathの参照。 +## canvas_tabのモードがDRAWINGでCP追加中にそのパスを保持する。 +## CP追加中ではない場合、nullとなる。 +var _current_adding_cp_path: Path = null + +## ドキュメントのマテリアル +var _document_material: ShaderMaterial + +## マウスを押している状態かどうか。 +var _mouse_pressing: bool = false +## マウスがmouse downしたあとに動いたかどうか。 +var _mouse_moving: bool = false +## ドラッグエリアが開始したかどうか。 +var _drag_area_start: bool = false +## ドラッグ開始時の位置。 +var _drag_start_position: Vector2 +## ドラッグ開始時にshiftやctrlを押していたか。 +var _drag_start_modifier: bool = false + +## 前フレームにcompositeが成功していたかどうか。 +var _prev_composite_succeeded: bool = false + + +func _ready() -> void: + Main.on_document_open.connect(_on_document_open) + Main.on_document_close.connect(_on_document_close) + Main.on_document_reload.connect(_on_document_reload) + Main.on_mirror_changed.connect(_update_mirror) + Main.on_start_edit_linear_gradient.connect(_start_edit_linear_gradient) + Main.on_start_edit_radial_gradient.connect(_start_edit_radial_gradient) + Main.on_end_edit_gradient.connect(_end_edit_gradient) + Main.on_selected_paths_changed.connect(_on_selected_paths_changed) + Main.on_update_control_segments.connect(_update_control_segments) + Main.on_path_shape_changed.connect(_on_paths_shape_changed) + _document_material = _document.material as ShaderMaterial + + +func _process(_delta: float) -> void: + if Main.document_opened: + _update_hover_controller() + _update_parameter_panel() + _update_control_point_scale() + _update_control_segment_scale() + _update_control_linear_gradient_scale() + _update_control_radial_gradient_scale() + _update_control_transform() + _update_visibility_control() + _set_document_size() + _update_layer_texture() + + var composite_succecceeded := Main.compositor.composite(Main.layers.root) + if not _prev_composite_succeeded and composite_succecceeded: + Main.emit_update_layer_list_tab() + _prev_composite_succeeded = composite_succecceeded + + +func _input(event: InputEvent) -> void: + if not Main.document_opened: + return + + if _current_tool == Tool.DRAWING: + _drawing_tool_input(event) + elif _current_tool == Tool.MANIPULATE: + _manipulate_tool_input(event) + else: + pass + + # ESCでレイヤー/パス/マテリアルの選択状態を解除 + if event is InputEventKey: + var key_event := event as InputEventKey + if key_event.keycode == KEY_ESCAPE: + if _current_tool == Tool.LINEAR_GRADIENT_EDITING or _current_tool == Tool.RADIAL_GRADIENT_EDITING: + _end_edit_gradient() + Main.layers.clear_selected_items() + Main.materials.clear_selected_index() + Main.commit_history() + + +## Drawingツールのときのイベントハンドリング。 +func _drawing_tool_input(event: InputEvent) -> void: + if event is InputEventMouseButton: + var mouse_button_event := event as InputEventMouseButton + var mouse_position := _control_layer.get_local_mouse_position() + + # Spaceの外をクリックした場合にはCPの追加などはしない + var space_rect := Rect2(Vector2.ZERO, _space.size) + if not space_rect.has_point(_space.get_local_mouse_position()): + return + + if mouse_button_event.button_index == MOUSE_BUTTON_LEFT and \ + mouse_button_event.pressed and \ + _space.get_viewport_rect().has_point(_space.get_local_mouse_position()): + if _current_adding_cp_path: + # ControlPointが3つ以上で最初のControlPointへのクリックだったら + # 現在CPを追加しているパスをclosedにしてCPの追加状態を終了する。 + if _control_points.size() > 2 and _control_points[0].get_hover(): + _current_adding_cp_path.change_closed(true) + _current_adding_cp_path = null + Main.commit_history() + else: + _current_adding_cp_path.add_control_point(mouse_position) + Main.commit_history() + + else: + # 新しくパスを追加してCPの追加モードを始める + _current_adding_cp_path = Main.layers.add_path() + if _current_adding_cp_path: + _current_adding_cp_path.add_control_point(mouse_position) + Main.commit_history() + _reset_control_points() + + # EnterキーでDrawing状態の解除 + if event is InputEventKey: + var key_event := event as InputEventKey + if key_event.keycode == KEY_ENTER: + if _current_adding_cp_path and _current_adding_cp_path.control_points.size() > 2: + _current_adding_cp_path = null + + +func _drag_area_update(start_pos: Vector2, end_pos: Vector2) -> void: + var min_x := start_pos.x if start_pos.x < end_pos.x else end_pos.x + var min_y := start_pos.y if start_pos.y < end_pos.y else end_pos.y + var max_x := start_pos.x if start_pos.x > end_pos.x else end_pos.x + var max_y := start_pos.y if start_pos.y > end_pos.y else end_pos.y + + var area_posision := Vector2(min_x, min_y) + var area_size := Vector2(max_x - min_x, max_y - min_y) + + _drag_area_panel.position = area_posision + _drag_area_panel.size = area_size + + +## Manipulateツールのときのイベントハンドリング。 +func _manipulate_tool_input(event: InputEvent) -> void: + var mouse_position := _space.get_local_mouse_position() + if event is InputEventMouseButton: + var mouse_button_event := event as InputEventMouseButton + + # mouse down + if not _mouse_pressing and mouse_button_event.pressed and mouse_button_event.button_index == MOUSE_BUTTON_LEFT: + _mouse_pressing = true + _mouse_moving = false + + if _current_tool == Tool.MANIPULATE: + # mouse down時にCP/CSをホバー中かどうか + var hover_cp := false + for cp in _control_points: + if cp.get_hover(): + hover_cp = true + break + var hover_cs := false + if not hover_cp: + for cs in _control_segments: + if cs.get_hover(): + hover_cs = true + break + + if hover_cp: + # CPをクリックしていたら + if mouse_button_event.ctrl_pressed or mouse_button_event.shift_pressed: + # モディファイアが押されていればhoverしているもののselectedを反転 + for cp in _control_points: + if cp.get_hover(): + cp.selected = not cp.selected + elif not _control_transform.is_hover(): + # モディファイアが押されていない場合、control_transformにホバー中ではない状態ならば、 + # hoverしているもの以外はselectedをクリアしてhoverしているものはselectedを反転 + for cp in _control_points: + if cp.get_hover(): + cp.selected = not cp.selected + else: + cp.selected = false + elif not hover_cs and not _control_transform.is_hover(): + # control_segmentやcontrol_transformにホバー中ではない状態で + # CP以外のところをクリックしたらドラッグエリア開始 + _drag_area_start = true + _drag_start_position = mouse_position + _drag_area_panel.size = Vector2.ZERO + _drag_area_panel.position = mouse_position + _drag_area_update(_drag_start_position, mouse_position) + + if mouse_button_event.ctrl_pressed or mouse_button_event.shift_pressed: + _drag_start_modifier = true + else: + _drag_start_modifier = false + + # mouse up + if _mouse_pressing and not mouse_button_event.pressed: + if _drag_area_start and _mouse_moving: + # ドラッグエリアが開始していて実際にドラッグされていた場合 + + # shiftやctrlを押していない場合はCPを一旦クリアする + if not _drag_start_modifier: + for cp in _control_points: + cp.selected = false + + # 矩形の範囲内のCPを選択する + var rect := Rect2((_drag_area_panel.position - _control_layer.position) / Main.viewport_scale, _drag_area_panel.size / Main.viewport_scale) + for cp in _control_points: + var pos := cp.get_control_position() + if rect.has_point(pos): + cp.selected = true + + _drag_area_panel.visible = false + elif _drag_area_start and not _mouse_moving: + # ドラッグエリアが開始しているがドラッグされていない場合 + # shiftやctrlを押していない場合はCPをクリアする + if not _drag_start_modifier: + for cp in _control_points: + cp.selected = false + + _drag_area_start = false + _mouse_pressing = false + _mouse_moving = false + + # ダブルクリック時 + if mouse_button_event.double_click and mouse_button_event.button_index == MOUSE_BUTTON_LEFT: + var space_rect := Rect2(Vector2.ZERO, _space.size) + if space_rect.has_point(_space.get_local_mouse_position()): + var click_position := _control_layer.get_local_mouse_position() + + # ホバー中のCPを削除 + var hover_cp := false + for cp in _control_points: + if cp.get_hover(): + cp.delete_cp() + Main.commit_history() + hover_cp = true + break + + # ホバー中のCSに点を挿入 + var hover_cs := false + for cs in _control_segments: + if cs.get_hover(): + cs.insert_cp(click_position) + Main.commit_history() + hover_cs = true + break + + # 何もホバー中でない場合、最後に選択したパスに新たにCPを追加 + if not hover_cp and not hover_cs: + if Main.layers.last_selected_path: + Main.layers.last_selected_path.add_control_point(click_position) + Main.commit_history() + + if event is InputEventMouseMotion: + # mouse drag + if _mouse_pressing: + _mouse_moving = true + + if _drag_area_start and _mouse_moving: + _drag_area_panel.visible = true + _drag_area_update(_drag_start_position, mouse_position) + + +## 選択中のパスが変更したときのコールバック。 +func _on_selected_paths_changed() -> void: + var selected := false + for path in Main.layers.selected_paths: + if not path.is_locked() and path == _current_adding_cp_path: + selected = true + break + if not selected: + _current_adding_cp_path = null + _reset_control_points() + _reset_control_segments() + _update_control_segments() + + +## 選択中のパスの形状が変更したときのコールバック。 +func _on_paths_shape_changed() -> void: + _reset_control_points() + _reset_control_segments() + _update_control_segments() + + +## PathのControlPointを追加する。 +func _add_path_control_points(path: Path) -> void: + for j in path.control_points.size(): + var cp := ControlPoint.new_control_point(path, j) + _control_points.append(cp) + _control_layer.add_child(cp) + cp.on_manipulate_finished.connect(_update_control_segments) + + +## PathのControlSegmentを追加する。 +func _add_path_control_segments(path: Path) -> void: + for j in path.segments.size(): + var cs := ControlSegment.new_control_segment(path, j) + _control_segments.append(cs) + _control_layer.add_child(cs) + cs.on_manipulate_finished.connect(_update_control_segments) + cs.set_segment(path.segments[j]) + + +## コントローラーのhover状態を更新する。 +func _update_hover_controller() -> void: + # hoverフラグをリセットする + for cp in _control_points: + cp.set_hover(false) + for cs in _control_segments: + cs.set_hover(false) + + # mouse overしている最初の一つをhover状態にする + for cp in _control_points: + if cp.mouse_over: + cp.set_hover(true) + return + for cs in _control_segments: + if cs.mouse_over: + cs.set_hover(true) + return + + +## パラメータ調整中のパラメータ表示を更新する。 +func _update_parameter_panel() -> void: + if _current_tool == Tool.PARAMETER_TUNING: + _parameter_panel.visible = true + _parameter_label1.text = "" + _parameter_label2.text = "" + for cp in _control_points: + if cp.get_hover() or cp.is_changing_parameter(): + _parameter_label1.text = "weight: " + str(cp.weight) + for cs in _control_segments: + if cs.get_hover() or cs.is_changing_parameter(): + _parameter_label1.text = "Psi: " + str(cs.psi) + _parameter_label2.text = "Phi: " + str(cs.phi) + else: + _parameter_panel.visible = false + _parameter_label1.text = "" + _parameter_label2.text = "" + + +## ドキュメントの表示倍率をControlPointに反映させる関数。 +func _update_control_point_scale() -> void: + for cp in _control_points: + cp.set_control_scale(Main.viewport_scale) + + +## ドキュメントの表示倍率をControlSegmentに反映させる関数。 +func _update_control_segment_scale() -> void: + for cs in _control_segments: + cs.set_control_scale(Main.viewport_scale) + + +## ドキュメントの表示倍率をControlLinearGradientに反映させる関数。 +func _update_control_linear_gradient_scale() -> void: + _control_linear_gradinet.set_control_scale(Main.viewport_scale) + + +## ドキュメントの表示倍率をControlRadialGradientに反映させる関数。 +func _update_control_radial_gradient_scale() -> void: + _control_radial_gradinet.set_control_scale(Main.viewport_scale) + + +## 描画のレイヤーテクスチャを更新する関数。 +func _update_layer_texture() -> void: + var tex := Main.compositor.root_composite_texture.texture + _document_material.set_shader_parameter("main_texture", tex) + + +## 現在選択中のレイヤーと現在のツールを確認してControlPointを作り直す。 +func _reset_control_points(recreate: bool = true) -> void: + if _current_tool == Tool.DRAWING: + # 現在のコントローラーを削除する + for cp in _control_points: + cp.queue_free() + _control_points.clear() + + # 現在描画中のPathについてコントローラーを作り直す。 + if _current_adding_cp_path: + _add_path_control_points(_current_adding_cp_path) + + # コントローラの設定のアップデート + for cp in _control_points: + cp.mode = ControlPoint.ControlMode.NOOP + elif _current_tool == Tool.MANIPULATE or _current_tool == Tool.PARAMETER_TUNING: + if recreate: + # 現在のコントローラーを削除する + for cp in _control_points: + cp.queue_free() + _control_points.clear() + + # 現在選択中のPathについてコントローラーを作り直す。 + for path in Main.layers.selected_paths: + if not path.is_locked(): + _add_path_control_points(path) + + # コントローラの設定のアップデート + if _current_tool == Tool.MANIPULATE: + for cp in _control_points: + cp.mode = ControlPoint.ControlMode.MOVE_POSITION + elif _current_tool == Tool.PARAMETER_TUNING: + for cp in _control_points: + cp.mode = ControlPoint.ControlMode.CHANGE_WEIGHT + + +## 現在選択中のレイヤーと現在のツールを確認してControlSegmentを作り直す。 +func _reset_control_segments(recreate: bool = true) -> void: + if _current_tool == Tool.DRAWING: + # 現在のコントローラーを削除する + for cs in _control_segments: + cs.queue_free() + _control_segments.clear() + elif _current_tool == Tool.MANIPULATE or _current_tool == Tool.PARAMETER_TUNING: + if recreate: + # 現在のコントローラーを削除する + for cs in _control_segments: + cs.queue_free() + _control_segments.clear() + + # 現在選択中のPathについてコントローラーを作り直す。 + for path in Main.layers.selected_paths: + if not path.is_locked(): + _add_path_control_segments(path) + + # コントローラーの設定のアップデート + if _current_tool == Tool.MANIPULATE: + for cs in _control_segments: + cs.mode = ControlSegment.ControlMode.NOOP + elif _current_tool == Tool.PARAMETER_TUNING: + for cs in _control_segments: + cs.mode = ControlSegment.ControlMode.CHANGE_PARAMETER + + +## ControlSegmentの形状をupdateする。 +func _update_control_segments() -> void: + if _current_tool == Tool.MANIPULATE or _current_tool == Tool.PARAMETER_TUNING: + var index := 0 + for path in Main.layers.selected_paths: + for s in path.segments: + if Main.is_manipulating: + _control_segments[index].control_hide() + else: + _control_points[index].control_show() + _control_segments[index].set_segment(s) + index += 1 + + +## ControlPointとControlSegmentをスペースバーを押している間は非表示にする。 +func _update_visibility_control() -> void: + if Input.is_key_pressed(KEY_SPACE): + for cp in _control_points: + cp.control_hide() + for cs in _control_segments: + cs.control_hide() + _control_transform.control_hide() + _control_linear_gradinet.control_hide() + _control_radial_gradinet.control_hide() + else: + for cp in _control_points: + cp.control_show() + for cs in _control_segments: + cs.control_show() + _control_transform.control_show() + _control_linear_gradinet.control_show() + _control_radial_gradinet.control_show() + + +## マニピュレート中かどうかを取得する。 +func get_manipulating() -> bool: + var is_manipulating := false + if _control_transform.is_manipulating(): + is_manipulating = true + if _control_linear_gradinet.is_manipulating(): + is_manipulating = true + if _control_radial_gradinet.is_manipulating(): + is_manipulating = true + if not is_manipulating: + for cp in _control_points: + if cp.is_manipulating(): + is_manipulating = true + break + if not is_manipulating: + for cs in _control_segments: + if cs.is_manipulating(): + is_manipulating = true + break + return is_manipulating + + +## control transformのCPを更新する。 +func _update_control_transform() -> void: + var selected_control_points: Array[ControlPoint] = [] + for cp in _control_points: + if cp.selected: + selected_control_points.append(cp) + cp.set_control_transform_hover(_control_transform.is_hover()) + _control_transform.set_control_points(selected_control_points) + + +## ドキュメントを閉じた状態にする。 +func _remove_controls() -> void: + # 現在のコントローラーを削除する + for cp in _control_points: + cp.queue_free() + _control_points.clear() + for cs in _control_segments: + cs.queue_free() + _control_segments.clear() + + +## ミラー状態を更新する。 +func _update_mirror() -> void: + if not Main.mirror: + _mirror_sprite.visible = true + _mirror_flip_sprite.visible = false + else: + _mirror_sprite.visible = false + _mirror_flip_sprite.visible = true + _reset_control_points() + _reset_control_segments() + + +## 線形グラデーションの編集を開始する。 +func _start_edit_linear_gradient(gradient_material: LinearGradientPaintMaterial) -> void: + _current_tool = Tool.LINEAR_GRADIENT_EDITING + _control_linear_gradinet.set_gradeint_material(gradient_material) + _control_linear_gradinet.visible = true + + _control_transform.control_hide() + for cp in _control_points: + cp.control_hide() + for cs in _control_segments: + cs.control_hide() + + +## 放射グラデーションの編集を開始する。 +func _start_edit_radial_gradient(gradient_material: RadialGradientPaintMaterial) -> void: + _current_tool = Tool.RADIAL_GRADIENT_EDITING + _control_radial_gradinet.set_gradeint_material(gradient_material) + _control_radial_gradinet.visible = true + + _control_transform.control_hide() + for cp in _control_points: + cp.control_hide() + for cs in _control_segments: + cs.control_hide() + + +## グラデーションの編集を終了する。 +func _end_edit_gradient() -> void: + if _current_tool == Tool.LINEAR_GRADIENT_EDITING or _current_tool == Tool.RADIAL_GRADIENT_EDITING: + _control_linear_gradinet.visible = false + _control_radial_gradinet.visible = false + _on_drawing_button_pressed() + + +## Documentのサイズを変更する。 +func _set_document_size() -> void: + _document.size = Main.document_size + _control_layer.size = Main.document_size + + +## ドキュメントが開いたときに呼ばれるコールバック。 +func _on_document_open() -> void: + _document.visible = true + _control_layer.visible = true + _current_adding_cp_path = null + if _current_tool == Tool.PARAMETER_TUNING: + _parameter_panel.visible = true + _reset_control_points() + _reset_control_segments() + _space.expand_document() + _control_linear_gradinet.visible = false + _control_radial_gradinet.visible = false + + +## ドキュメントが閉じたときに呼ばれるコールバック。 +func _on_document_close() -> void: + _document.visible = false + _control_layer.visible = false + _parameter_panel.visible = false + _current_adding_cp_path = null + _remove_controls() + _control_linear_gradinet.visible = false + _control_radial_gradinet.visible = false + + +## ドキュメントがリロードしたときに呼ばれるコールバック。 +func _on_document_reload() -> void: + _document.visible = true + _control_layer.visible = true + _current_adding_cp_path = null + if _current_tool == Tool.PARAMETER_TUNING: + _parameter_panel.visible = true + _reset_control_points() + _reset_control_segments() + _control_linear_gradinet.visible = false + _control_radial_gradinet.visible = false + + +## DRAWINGツールボタンを押したときのコールバック。 +func _on_drawing_button_pressed() -> void: + if not _current_tool == Tool.DRAWING: + _current_tool = Tool.DRAWING + _drawing_tool_highlight.visible = true + _manipulate_tool_highlight.visible = false + _parameter_tuning_tool_highlight.visible = false + if not _drag_start_modifier: + for cp in _control_points: + cp.selected = false + _reset_control_points() + _reset_control_segments() + _control_linear_gradinet.visible = false + _control_radial_gradinet.visible = false + + +## MANIPULATEツールボタンを押したときのコールバック。 +func _on_direct_selection_button_pressed() -> void: + var prev_tool := _current_tool + if not _current_tool == Tool.MANIPULATE: + _current_tool = Tool.MANIPULATE + _drawing_tool_highlight.visible = false + _manipulate_tool_highlight.visible = true + _parameter_tuning_tool_highlight.visible = false + _current_adding_cp_path = null + if prev_tool == Tool.PARAMETER_TUNING: + _reset_control_points(false) + _reset_control_segments(false) + else: + _reset_control_points() + _reset_control_segments() + _control_linear_gradinet.visible = false + _control_radial_gradinet.visible = false + + +## PARAMETER_TUNINGツールボタンを押したときのコールバック。 +func _on_parameter_tuning_button_pressed() -> void: + var prev_tool := _current_tool + if not _current_tool == Tool.PARAMETER_TUNING: + _current_tool = Tool.PARAMETER_TUNING + _drawing_tool_highlight.visible = false + _manipulate_tool_highlight.visible = false + _parameter_tuning_tool_highlight.visible = true + _current_adding_cp_path = null + if not _drag_start_modifier: + for cp in _control_points: + cp.selected = false + if prev_tool == Tool.MANIPULATE: + _reset_control_points(false) + _reset_control_segments(false) + else: + _reset_control_points() + _reset_control_segments() + _control_linear_gradinet.visible = false + _control_radial_gradinet.visible = false + + +## ズームリセットボタンが押された際のコールバック。 +func _on_zoom_reset_button_pressed() -> void: + _space.reset_document_scale() + + +## expandボタンが押された際のコールバック。 +func _on_expand_button_pressed() -> void: + _space.expand_document() + + +## ミラーボタンを押したときのコールバック。 +func _on_mirror_button_pressed() -> void: + Main.mirror = not Main.mirror + + +## ControlTransformの操作が完了するたびに呼ばれるコールバック。 +func _on_control_transform_on_manipulate_finished() -> void: + _reset_control_segments() + _update_control_segments() diff --git a/godot/src/tab/layer_list_tab.gd b/godot/src/tab/layer_list_tab.gd new file mode 100644 index 0000000..6366712 --- /dev/null +++ b/godot/src/tab/layer_list_tab.gd @@ -0,0 +1,743 @@ +## レイヤーのリストのタブのクラス。 +## レイヤーのリストの並べ替えのDnDの処理を含む。 +class_name LayerListTab +extends PanelContainer + + +@onready var _blend_mode_menu: MenuButton = %BlendModeMenu +@onready var _alpha_slider: Slider = %AlphaSlider +@onready var _alpha_spin_box: SpinBox = %AlphaSpinBox +@onready var _add_button: Button = %AddButton +@onready var _add_fill_button: Button = %AddFillButton +@onready var _add_group_button: Button = %AddGroupButton +@onready var _delete_button: Button = %DeleteButton +@onready var _clip_button: Button = %ClipButton +@onready var _clip_panel: Panel = %ClipButton/Panel +@onready var _locked_button: Button = %LockButton +@onready var _unlocked: Sprite2D = %LockButton/UnLocked +@onready var _locked: Sprite2D = %LockButton/Locked +@onready var _locked_panel: Panel = %LockButton/Panel +@onready var _open_close_button: Button = %OpenCloseButton +@onready var _open_path: Sprite2D = %OpenCloseButton/Open +@onready var _close_path: Sprite2D = %OpenCloseButton/Close +@onready var _boolean_menu: MenuButton = %BooleanMenu +@onready var _boolean_union: Sprite2D = %BooleanMenu/Union +@onready var _boolean_diff: Sprite2D = %BooleanMenu/Diff +@onready var _boolean_intersect: Sprite2D = %BooleanMenu/Intersect +@onready var _boolean_xor: Sprite2D = %BooleanMenu/Xor + +@onready var _scroll_container: ScrollContainer = %ScrollContainer +@onready var _container: VBoxContainer = %ScrollContainer/ListContainer +@onready var _dnd_cursor: ListDropCursor = %ScrollContainer/ListContainer/ListDropCursor + +## レイヤーリストの要素。 +var _items: Array[LayerListItem] = [] + +## 最後にalphaを変更した時刻。 +var _last_alpha_changed_time: int = -1 +## alpha変更があってからcommitしたかどうか。 +var _last_alpha_changed_commit: bool = true + +## layer_listがdirtyかどうか。 +var _is_layer_list_dirty: bool = false + + +func _ready() -> void: + Main.on_update_layer_list_tab.connect(_set_dirty) + Main.on_locale_changed.connect(_set_dirty) + + _boolean_menu.get_popup().id_pressed.connect(_change_boolean) + _blend_mode_menu.get_popup().id_pressed.connect(_change_blend_mode) + + _update_layer_list_tab_buttons() + + +func _process(_delta: float) -> void: + if not Main.document_opened: + _update_layer_list() + return + + # ドラッグ中ならマスカーソルの一でスクロールを動かす + if Main.layers.dragging_root_layers.size() > 0 or Main.layers.dragging_paths.size() > 0: + var mouse_position := _scroll_container.get_local_mouse_position() + + var top_rect := Rect2(Vector2.ZERO, Vector2(_scroll_container.size.x, 32)) + var bottom_rect := Rect2(Vector2(0, _scroll_container.size.y - 32), Vector2(_scroll_container.size.x, 32)) + + if top_rect.has_point(mouse_position): + _scroll_container.scroll_vertical -= 2 + if bottom_rect.has_point(mouse_position): + _scroll_container.scroll_vertical += 2 + + # アルファが最後に変更されてから500ms経っていて、かつ、コミットされていなければコミットする。 + if not _last_alpha_changed_commit and Time.get_ticks_msec() - _last_alpha_changed_time > 500: + _last_alpha_changed_commit = true + Main.commit_history() + + # dirtyだったら更新する + if _is_layer_list_dirty: + # レイヤーのサムネのミップマップ生成が遅いため、更新頻度の高いコントローラーの操作中は更新を止める + if not Main.is_manipulating: + _update_layer_list() + _is_layer_list_dirty = false + + +func _notification(what: int) -> void: + match what: + NOTIFICATION_DRAG_END: + _drag_end() + + +func _get_drag_data(_at_position: Vector2) -> Variant: + # ドラッグ開始時に選択していたアイテムをクリックしてドラッグしているか確認する + Main.layers.drag_start() + _update_layer_list() + + var drag_item_mouse_on := false + for item in _items: + if item.dragging and item.mouse_over: + drag_item_mouse_on = true + break + if not drag_item_mouse_on: + Main.layers.drag_end() + _set_dirty() + return + + if Main.layers.dragging_root_layers.size() > 0: + return Main.layers.dragging_root_layers[0] + else: + return Main.layers.dragging_paths[0] + + +func _can_drop_data(_at_position: Vector2, data: Variant) -> bool: + if data is PaintLayer: + _dnd_cursor.visible = true + + var cursor_index := _container.get_child_count() - 1 + var depth := 0 + for index in _items.size(): + var item := _items[index] + var next_item := _items[index + 1] if index != _items.size() - 1 else null + if item.mouse_over_top: + if item.dragging: + cursor_index = -1 + break + if item.is_path(): + cursor_index = -1 + else: + cursor_index = index + depth = item.depth + elif item.mouse_over_bottom: + if item.dragging: + cursor_index = -1 + break + if item.is_path(): + if next_item: + if next_item.is_path(): + cursor_index = -1 + else: + cursor_index = index + 1 + depth = next_item.depth + else: + cursor_index = index + 1 + depth = 0 + elif item.is_path_layer(): + if item.is_open(): + if item.is_path_layer_empty(): + cursor_index = index + 1 + depth = item.depth + else: + cursor_index = -1 + else: + cursor_index = index + 1 + depth = item.depth + elif item.is_fill_layer(): + cursor_index = index + 1 + depth = item.depth + elif item.is_group_layer(): + if item.is_open(): + cursor_index = index + 1 + depth = item.depth + 1 + else: + cursor_index = index + 1 + depth = item.depth + _dnd_cursor.set_depth(depth) + + # ドラッグ先を示すカーソルを挿入する + for child in _container.get_children(): + _container.remove_child(child) + for index in _items.size(): + var item := _items[index] + if index == cursor_index: + _container.add_child(_dnd_cursor) + _container.add_child(item) + if cursor_index == _items.size(): + _container.add_child(_dnd_cursor) + + return true + + if data is Path: + _dnd_cursor.visible = true + + var cursor_index := _container.get_child_count() - 1 + var depth := 0 + for index in _items.size(): + var prev_item := _items[index - 1] if index != 0 else null + var item := _items[index] + if item.mouse_over_top: + if item.dragging: + cursor_index = -1 + break + if item.is_path(): + cursor_index = index + depth = item.depth + else: + if prev_item: + if prev_item.is_path(): + cursor_index = index + depth = prev_item.depth + else: + cursor_index = -1 + else: + cursor_index = -1 + elif item.mouse_over_bottom: + if item.dragging: + cursor_index = -1 + break + if item.is_path(): + cursor_index = index + 1 + depth = item.depth + elif item.is_path_layer(): + if item.is_open(): + cursor_index = index + 1 + depth = item.depth + 1 + else: + cursor_index = -1 + else: + cursor_index = -1 + _dnd_cursor.set_depth(depth) + + # ドラッグ先を示すカーソルを挿入する + for child in _container.get_children(): + _container.remove_child(child) + for index in _items.size(): + var item := _items[index] + if index == cursor_index: + _container.add_child(_dnd_cursor) + _container.add_child(item) + if cursor_index == _items.size(): + _container.add_child(_dnd_cursor) + + return true + + _dnd_cursor.visible = false + return false + + +## ドラッグが終了したときに呼び出す関数。 +func _drag_end() -> void: + # レイヤーのドラッグ中 + if Main.layers.dragging_root_layers.size() > 0: + var insert_at := func(item: LayerListItem, index: int, top: bool) -> void: + var insert := 0 + var parent: GroupPaintLayer = null + if item.is_path(): + # レベルが2つ上のGroupPaintLayerまで逆にたどって親を見つける + for i in index: + if _items[index - i - 1].depth == item.depth - 2: + parent = _items[index - i - 1].get_group_layer() + break + # 一つ上のレベルで後ろにあるやつを数えてinsertの数を調べる + for i in range(index + 1, _items.size()): + if _items[i].depth == item.depth - 1: + insert += 1 + elif _items[i].depth > item.depth - 1: + pass + else: + break + else: + # レベルが一つ上のGroupPaintLayerまで逆にたどって親を見つける + for i in index: + if _items[index - i - 1].depth == item.depth - 1: + parent = _items[index - i - 1].get_group_layer() + break + # 同じレベルで後ろにあるやつを数えてinsertの数を調べる + for i in range(index + 1, _items.size()): + if _items[i].depth == item.depth: + insert += 1 + elif _items[i].depth > item.depth: + pass + else: + break + if top: + insert += 1 + Main.layers.drag_move_layer(insert, parent) + Main.commit_history() + + var inserted := false + for index in _items.size(): + var item := _items[index] + var next_item := _items[index + 1] if index != _items.size() - 1 else null + if item.mouse_over_top: + if item.dragging: + break + if item.is_path(): + break + else: + insert_at.call(item, index, true) + inserted = true + break + elif item.mouse_over_bottom: + if item.dragging: + break + if item.is_path(): + if next_item: + if next_item.is_path(): + break + else: + insert_at.call(item, index, false) + inserted = true + break + else: + insert_at.call(item, index, false) + inserted = true + break + elif item.is_path_layer(): + if item.is_open(): + if item.is_path_layer_empty(): + insert_at.call(item, index, false) + inserted = true + break + else: + pass + else: + insert_at.call(item, index, false) + inserted = true + break + elif item.is_fill_layer(): + insert_at.call(item, index, false) + inserted = true + break + elif item.is_group_layer(): + if item.is_open(): + var group_layer := item.get_group_layer() + if item.is_group_layer_empty(): + Main.layers.drag_move_layer(0, group_layer) + Main.commit_history() + else: + Main.layers.drag_move_layer(group_layer.child_layers.size(), group_layer) + Main.commit_history() + inserted = true + break + else: + insert_at.call(item, index, false) + inserted = true + break + if not inserted: + Main.layers.drag_move_layer(0, null) + + # パスのドラッグ中 + elif Main.layers.dragging_paths.size() > 0: + var insert_at := func(item: LayerListItem, index: int, top_of_path: bool) -> void: + var insert := 0 + var parent: PathPaintLayer = null + # レベルが一つ上PathpPaintLayerまで逆にたどって親を見つける + for i in index: + if _items[index - i - 1].depth == item.depth - 1: + parent = _items[index - i - 1].get_path_layer() + break + # 同じレベルで後ろにあるやつを数えてinsertの数を調べる + for i in range(index + 1, _items.size()): + if _items[i].depth == item.depth: + insert += 1 + elif _items[i].depth > item.depth: + pass + else: + break + if top_of_path: + insert += 1 + Main.layers.drag_move_path(insert, parent) + + for index in _items.size(): + var prev_item := _items[index - 1] if index != 0 else null + var item := _items[index] + if item.mouse_over_top: + if item.dragging: + break + if item.is_path(): + insert_at.call(item, index, true) + break + else: + if prev_item: + if prev_item.is_path(): + insert_at.call(prev_item, index - 1, false) + break + else: + break + else: + break + elif item.mouse_over_bottom: + if item.dragging: + break + if item.is_path(): + insert_at.call(item, index, false) + break + elif item.is_path_layer(): + if item.is_open(): + var path_layer := item.get_path_layer() + if item.is_path_layer_empty(): + Main.layers.drag_move_path(0, path_layer) + Main.commit_history() + else: + Main.layers.drag_move_path(path_layer.paths.size(), path_layer) + Main.commit_history() + break + else: + break + else: + break + + _dnd_cursor.visible = false + Main.layers.drag_end() + _set_dirty() + + +## レイヤーリストタブの上部のボタンを更新する。 +func _update_layer_list_tab_buttons() -> void: + if not Main.document_opened: + _blend_mode_menu.text = tr("BLEND_MODE_NORMAL") + _add_button.disabled = true + _add_fill_button.disabled = true + _add_group_button.disabled = true + _locked_button.disabled = true + _locked_panel.visible = false + _clip_panel.visible = false + _blend_mode_menu.disabled = true + _alpha_slider.editable = false + _alpha_spin_box.editable = false + _delete_button.disabled = true + _clip_button.disabled = true + _open_close_button.disabled = true + _boolean_menu.disabled = true + _alpha_slider.set_value_no_signal(100) + _alpha_spin_box.set_value_no_signal(100) + return + + if Main.layers.last_selected_layer == null: + _blend_mode_menu.text = tr("BLEND_MODE_NORMAL") + _locked_button.disabled = true + _locked_panel.visible = false + _clip_panel.visible = false + _blend_mode_menu.disabled = true + _alpha_slider.editable = false + _alpha_spin_box.editable = false + _delete_button.disabled = true + _clip_button.disabled = true + _open_close_button.disabled = true + _boolean_menu.disabled = true + _alpha_slider.set_value_no_signal(100) + _alpha_spin_box.set_value_no_signal(100) + return + + _add_button.disabled = false + _add_fill_button.disabled = false + _add_group_button.disabled = false + _locked_button.disabled = false + + var layer := Main.layers.last_selected_layer + var path := Main.layers.last_selected_path + + # blend mode + match layer.blend_mode: + 0: + _blend_mode_menu.text = tr("BLEND_MODE_NORMAL") + 1: + _blend_mode_menu.text = tr("BLEND_MODE_ADD") + 2: + _blend_mode_menu.text = tr("BLEND_MODE_MULTIPLY") + 3: + _blend_mode_menu.text = tr("BLEND_MODE_SCREEN") + 4: + _blend_mode_menu.text = tr("BLEND_MODE_OVERLAY") + + # alpha + _alpha_slider.set_value_no_signal(layer.alpha) + _alpha_spin_box.set_value_no_signal(layer.alpha) + + # clip + if layer.clipped: + _clip_panel.visible = true + else: + _clip_panel.visible = false + + # lock + if Main.layers.all_locked_item_in_selected(): + _unlocked.visible = false + _locked.visible = true + else: + _unlocked.visible = true + _locked.visible = false + if Main.layers.any_locked_item_in_selected(): + _locked_panel.visible = true + _blend_mode_menu.disabled = true + _alpha_slider.editable = false + _alpha_spin_box.editable = false + _delete_button.disabled = true + _clip_button.disabled = true + _open_close_button.disabled = true + _boolean_menu.disabled = true + else: + _locked_panel.visible = false + _blend_mode_menu.disabled = false + _alpha_slider.editable = true + _alpha_spin_box.editable = true + _delete_button.disabled = false + _clip_button.disabled = false + _open_close_button.disabled = false + _boolean_menu.disabled = false + + + if path: + # pathのopen/close + if path.closed: + _open_path.visible = false + _close_path.visible = true + else: + _open_path.visible = true + _close_path.visible = false + + # pathのboolean + if path.boolean == Path.Boolean.Union: + _boolean_union.visible = true + _boolean_diff.visible = false + _boolean_intersect.visible = false + _boolean_xor.visible = false + elif path.boolean == Path.Boolean.Diff: + _boolean_union.visible = false + _boolean_diff.visible = true + _boolean_intersect.visible = false + _boolean_xor.visible = false + elif path.boolean == Path.Boolean.Intersect: + _boolean_union.visible = false + _boolean_diff.visible = false + _boolean_intersect.visible = true + _boolean_xor.visible = false + elif path.boolean == Path.Boolean.Xor: + _boolean_union.visible = false + _boolean_diff.visible = false + _boolean_intersect.visible = false + _boolean_xor.visible = true + + +## PathPaintLayerの中の表示されるitemの数を計算する。 +func _count_visible_items_in_path_layer(path_layer: PathPaintLayer) -> int: + if not path_layer.collapsed: + return path_layer.paths.size() + else: + return 0 + + +## GroupPaintLayerの中の表示されるitemの数を計算する。 +func _count_visible_items_in_group_layer(group_layer: GroupPaintLayer) -> int: + var count := 0 + if not group_layer.collapsed: + for layer in group_layer.child_layers: + count += 1 + if layer is PathPaintLayer: + var path_layer := layer as PathPaintLayer + count += _count_visible_items_in_path_layer(path_layer) + if layer is GroupPaintLayer: + var group := layer as GroupPaintLayer + count += _count_visible_items_in_group_layer(group) + return count + + +## 表示されるレイヤーの数を計算する。 +func _count_visible_items() -> int: + var count := 0 + for layer in Main.layers.root: + count += 1 + if layer is PathPaintLayer: + var path_layer := layer as PathPaintLayer + count += _count_visible_items_in_path_layer(path_layer) + if layer is GroupPaintLayer: + var group_layer := layer as GroupPaintLayer + count += _count_visible_items_in_group_layer(group_layer) + return count + + +## PathPaintLayerのパスを_itemに追加する。 +func _add_path_layer_item(path_layer: PathPaintLayer, depth: int, items_index: int) -> int: + if not path_layer.collapsed: + for index in path_layer.paths.size(): + var path := path_layer.paths[path_layer.paths.size() - index - 1] + var item := _items[items_index] + item.init_path(path, depth) + items_index += 1 + return items_index + + +## PathPaintLayerのレイヤーを_itemに追加する。 +func _add_group_layer_item(group_layer: GroupPaintLayer, depth: int, items_index: int) -> int: + if not group_layer.collapsed: + for index in group_layer.child_layers.size(): + var layer := group_layer.child_layers[group_layer.child_layers.size() - index - 1] + var item := _items[items_index] + item.init_layer(layer, depth) + items_index += 1 + + if layer is PathPaintLayer: + var path_layer := layer as PathPaintLayer + items_index = _add_path_layer_item(path_layer, depth + 1, items_index) + + if layer is GroupPaintLayer: + var group := layer as GroupPaintLayer + items_index = _add_group_layer_item(group, depth + 1, items_index) + return items_index + + +## layer_listをdirtyにマークする。 +func _set_dirty() -> void: + _is_layer_list_dirty = true + + +## レイヤーリストを再構築し更新する。 +func _update_layer_list() -> void: + if not Main.document_opened: + # rレイヤーリストを空にする + while _items.size() > 0: + _items[_items.size() - 1].queue_free() + _items.remove_at(_items.size() - 1) + # 上部のボタンを更新する + _update_layer_list_tab_buttons() + return + + var count := _count_visible_items() + while _items.size() > count: + _items[_items.size() - 1].queue_free() + _items.remove_at(_items.size() - 1) + while _items.size() < count: + var item := LayerListItem.new_item() + _items.append(item) + _container.add_child(item) + + var items_index := 0 + + # レイヤーリストを再構築する + for index in Main.layers.root.size(): + var layer := Main.layers.root[Main.layers.root.size() - index - 1] + var item := _items[items_index] + item.init_layer(layer, 0) + items_index += 1 + + if layer is PathPaintLayer: + var path_layer := layer as PathPaintLayer + items_index = _add_path_layer_item(path_layer, 1, items_index) + + if layer is GroupPaintLayer: + var group_layer := layer as GroupPaintLayer + items_index = _add_group_layer_item(group_layer, 1, items_index) + + # 上部のボタンを更新する + _update_layer_list_tab_buttons() + + +## 現在選択中のパスのBooleanを切り替える。 +func _change_boolean(id: int) -> void: + var boolean := Path.Boolean.Union + match id: + 1: + boolean = Path.Boolean.Diff + 2: + boolean = Path.Boolean.Intersect + 3: + boolean = Path.Boolean.Xor + for path in Main.layers.selected_paths: + path.change_boolean(boolean) + Main.emit_update_layer_list_tab() + + Main.commit_history() + + +func _change_blend_mode(id: int) -> void: + var blend_mode := PaintLayer.BlendMode.Normal + match id: + 1: + blend_mode = PaintLayer.BlendMode.Add + 2: + blend_mode = PaintLayer.BlendMode.Multiply + 3: + blend_mode = PaintLayer.BlendMode.Screen + 4: + blend_mode = PaintLayer.BlendMode.Overlay + Main.layers.set_blend_mode_of_selected_layer(blend_mode) + + Main.commit_history() + + +## アルファを調整中かどうか。 +func is_manipulating_alpha() -> bool: + return not _last_alpha_changed_commit + + +## レイヤー追加ボタンを押したときのコールバック。 +func _on_add_button_button_up() -> void: + Main.layers.add_path_layer() + + Main.commit_history() + + +## 塗り頭レイヤー追加ボタンを押したときのコールバック。 +func _on_add_fill_button_button_up() -> void: + Main.layers.add_fill_layer() + + Main.commit_history() + + +## グループレイヤー追加ボタンを押したときのコールバック。 +func _on_add_group_button_button_up() -> void: + Main.layers.add_group_layer() + + Main.commit_history() + + +## レイヤー削除ボタンを押したときのコールバック。 +func _on_delete_button_button_up() -> void: + Main.layers.delete_selected_layer_path() + + Main.commit_history() + + +## レイヤークリッピングボタンを押したときのコールバック。 +func _on_clip_button_button_up() -> void: + Main.layers.change_clipping_of_selected_layer() + + Main.commit_history() + + +## レイヤーロックボタンを押したときのコールバック。 +func _on_lock_button_button_up() -> void: + Main.layers.change_locked_of_selected_item() + + Main.commit_history() + + +## パスのopen/close変更ボタンを押したときのコールバック。 +func _on_open_close_button_button_up() -> void: + Main.layers.change_closed_of_selected_path() + + Main.commit_history() + + +## アルファ値のスライダーの値が変わったときのコールバック。 +func _on_alpha_slider_value_changed(value: float) -> void: + Main.layers.set_alpha_of_selected_layer(int(value)) + _last_alpha_changed_time = Time.get_ticks_msec() + _last_alpha_changed_commit = false + + +## アルファ値のSpinBoxの値が変わったときのコールバック。 +func _on_alpha_spin_box_value_changed(value: float) -> void: + Main.layers.set_alpha_of_selected_layer(int(value)) + _last_alpha_changed_time = Time.get_ticks_msec() + _last_alpha_changed_commit = false diff --git a/godot/src/tab/layer_setting_tab.gd b/godot/src/tab/layer_setting_tab.gd new file mode 100644 index 0000000..c8851cb --- /dev/null +++ b/godot/src/tab/layer_setting_tab.gd @@ -0,0 +1,296 @@ +## レイヤー設定タブを扱うクラス。 +class_name LayerSettingTab +extends Node + + +@onready var _fill_material: HBoxContainer = %FillMaterial +@onready var _fill_empty_panel: Panel = %FillMaterial/EmptyButton/Panel +@onready var _fill_fill_panel: Panel = %FillMaterial/FillButton/Panel +@onready var _fill_button: Button = %FillMaterial/ColorButton +@onready var _fill_color: Panel = %FillMaterial/ColorButton/HBoxContainer/Color +@onready var _fill_label: Label = %FillMaterial/ColorButton/HBoxContainer/Label +@onready var _fill_popup: MaterialSelectPopup = %FillMaterial/ColorButton/MaterialSelectPopup + +@onready var _line_material: HBoxContainer = %LineMaterial +@onready var _line_empty_panel: Panel = %LineMaterial/EmptyButton/Panel +@onready var _line_fill_panel: Panel = %LineMaterial/FillButton/Panel +@onready var _line_button: Button = %LineMaterial/ColorButton +@onready var _line_color: Panel = %LineMaterial/ColorButton/HBoxContainer/Color +@onready var _line_label: Label = %LineMaterial/ColorButton/HBoxContainer/Label +@onready var _line_popup: MaterialSelectPopup = %LineMaterial/ColorButton/MaterialSelectPopup + +@onready var _line_width: HBoxContainer = %LineWidth +@onready var _line_width_slider: HSlider = %LineWidth/HSlider +@onready var _line_width_spin_box: SpinBox = %LineWidth/SpinBox + +@onready var _fill_layer_material: HBoxContainer = %FillLayerMaterial +@onready var _fill_layer_button: Button = %FillLayerMaterial/ColorButton +@onready var _fill_layer_color: Panel = %FillLayerMaterial/ColorButton/HBoxContainer/Color +@onready var _fill_layer_label: Label = %FillLayerMaterial/ColorButton/HBoxContainer/Label +@onready var _fill_layer_popup: MaterialSelectPopup = %FillLayerMaterial/ColorButton/MaterialSelectPopup + +## マテリアルmissing時の表示用マテリアル。 +@export var _missing_material: ShaderMaterial +## マテリアルの単一色の表示マテリアル。 +@export var _color_material: ShaderMaterial +## マテリアルの線形グラデーションの表示マテリアル。 +@export var _linear_gradient_material: ShaderMaterial +## マテリアルの放射グラデーションの表示マテリアル。 +@export var _radial_gradient_material: ShaderMaterial + +## 最後に線幅を変更した時刻。 +var _last_line_width_changed_time: int = -1 +## 線幅変更があってからcommitしたかどうか。 +var _last_line_width_changed_commit: bool = true + + +func _process(_delta: float) -> void: + if not Main.document_opened: + _fill_material.visible = false + _line_material.visible = false + _line_width.visible = false + _fill_layer_material.visible = false + _last_line_width_changed_commit = true + return + + # 現在選択中のレイヤーが存在しない場合は、表示を消す。 + if Main.layers.selected_layers.size() == 0: + _fill_material.visible = false + _line_material.visible = false + _line_width.visible = false + _fill_layer_material.visible = false + _last_line_width_changed_commit = true + return + + # 最後に選択されたレイヤーの情報を表示する + var last_selected_layer := Main.layers.last_selected_layer + if last_selected_layer is PathPaintLayer: + var path_layer := last_selected_layer as PathPaintLayer + + _fill_material.visible = true + _line_material.visible = true + _line_width.visible = true + _fill_layer_material.visible = false + + # fillの設定 + if path_layer.filled: + _fill_empty_panel.visible = true + _fill_fill_panel.visible = false + else: + _fill_empty_panel.visible = false + _fill_fill_panel.visible = true + var fill_material := Main.materials.get_material(path_layer.fill_material_id) + if fill_material is ColorPaintMaterial: + var color_material := fill_material as ColorPaintMaterial + var material := _color_material.duplicate() as ShaderMaterial + material.set_shader_parameter("fill_color", color_material.color) + _fill_color.material = material + elif fill_material is LinearGradientPaintMaterial: + var gradient_material := fill_material as LinearGradientPaintMaterial + var material := _linear_gradient_material.duplicate() as ShaderMaterial + material.set_shader_parameter("gradient_texture", gradient_material.gradient_texture) + _fill_color.material = material + elif fill_material is RadialGradientPaintMaterial: + var gradient_material := fill_material as RadialGradientPaintMaterial + var material := _radial_gradient_material.duplicate() as ShaderMaterial + material.set_shader_parameter("gradient_texture", gradient_material.gradient_texture) + _fill_color.material = material + elif fill_material == null: + _fill_color.material = _missing_material + _fill_label.text = Main.materials.get_material_name(path_layer.fill_material_id) + + # lineの設定 + if path_layer.outlined: + _line_empty_panel.visible = true + _line_fill_panel.visible = false + else: + _line_empty_panel.visible = false + _line_fill_panel.visible = true + var line_material := Main.materials.get_material(path_layer.outline_material_id) + if line_material is ColorPaintMaterial: + var color_material := line_material as ColorPaintMaterial + var material := _color_material.duplicate() as ShaderMaterial + material.set_shader_parameter("fill_color", color_material.color) + _line_color.material = material + elif line_material is LinearGradientPaintMaterial: + var gradient_material := line_material as LinearGradientPaintMaterial + var material := _linear_gradient_material.duplicate() as ShaderMaterial + material.set_shader_parameter("gradient_texture", gradient_material.gradient_texture) + _line_color.material = material + elif line_material is RadialGradientPaintMaterial: + var gradient_material := line_material as RadialGradientPaintMaterial + var material := _radial_gradient_material.duplicate() as ShaderMaterial + material.set_shader_parameter("gradient_texture", gradient_material.gradient_texture) + _line_color.material = material + elif line_material == null: + _line_color.material = _missing_material + _line_label.text = Main.materials.get_material_name(path_layer.outline_material_id) + + # line widthの設定 + _line_width_slider.set_value_no_signal(path_layer.outline_width) + _line_width_spin_box.set_value_no_signal(path_layer.outline_width) + if path_layer.outlined: + _line_width_slider.editable = true + _line_width_spin_box.editable = true + else: + _line_width_slider.editable = false + _line_width_spin_box.editable = false + + elif last_selected_layer is FillPaintLayer: + var fill_layer := last_selected_layer as FillPaintLayer + + _fill_material.visible = false + _line_material.visible = false + _line_width.visible = false + _fill_layer_material.visible = true + + var fill_material := Main.materials.get_material(fill_layer.fill_material_id) + if fill_material is ColorPaintMaterial: + var color_material := fill_material as ColorPaintMaterial + var material := _color_material.duplicate() as ShaderMaterial + material.set_shader_parameter("fill_color", color_material.color) + _fill_layer_color.material = material + elif fill_material is LinearGradientPaintMaterial: + var gradient_material := fill_material as LinearGradientPaintMaterial + var material := _linear_gradient_material.duplicate() as ShaderMaterial + material.set_shader_parameter("gradient_texture", gradient_material.gradient_texture) + _fill_layer_color.material = material + elif fill_material is RadialGradientPaintMaterial: + var gradient_material := fill_material as RadialGradientPaintMaterial + var material := _radial_gradient_material.duplicate() as ShaderMaterial + material.set_shader_parameter("gradient_texture", gradient_material.gradient_texture) + _fill_layer_color.material = material + else: + _fill_layer_color.material = _missing_material + _fill_layer_label.text = Main.materials.get_material_name(fill_layer.fill_material_id) + + elif last_selected_layer is GroupPaintLayer: + _fill_material.visible = false + _line_material.visible = false + _line_width.visible = false + _fill_layer_material.visible = false + + # 線幅が最後に変更されてから500ms経っていて、かつ、コミットされていなければコミットする。 + if not _last_line_width_changed_commit and Time.get_ticks_msec() - _last_line_width_changed_time > 500: + _last_line_width_changed_commit = true + Main.commit_history() + + +## 線幅を調整中かどうか。 +func is_manipulating_line_width() -> bool: + return not _last_line_width_changed_commit + + +## 塗りをemptyにするボタンを押したときのコールバック。 +func _on_empty_button_button_down() -> void: + Main.layers.set_fill_of_selected_layers(false) + Main.commit_history() + + +## 塗りをfillにするボタンを押したときのコールバック。 +func _on_fill_button_button_down() -> void: + Main.layers.set_fill_of_selected_layers(true) + Main.commit_history() + + +## 塗りの色のボタンを押したときのコールバック。 +func _on_color_button_button_down() -> void: + _fill_popup.show() + + var pos := _fill_button.get_screen_position() + Vector2(0, 44) + var popup_size := _fill_popup.get_visible_rect().size + + # get safe area + var safe_area := DisplayServer.get_display_safe_area() + + # clamp pos + if pos.x + popup_size.x > safe_area.position.x + safe_area.size.x: + pos.x = safe_area.position.x + safe_area.size.x - popup_size.x + if pos.y + popup_size.y > safe_area.position.y + safe_area.size.y: + pos.y = safe_area.position.y + safe_area.size.y - popup_size.y + + _fill_popup.position = pos + + +## 塗りのマテリアルを選択したときのコールバック。 +func _on_material_list_popup_on_material_selected(material_id: String) -> void: + Main.layers.set_fill_material_of_selected_path_layers(material_id) + Main.commit_history() + + +## 線をemptyにするボタンを押したときのコールバック。 +func _on_line_empty_button_button_down() -> void: + Main.layers.set_line_of_selected_layers(false) + Main.commit_history() + + +## 線をfillにするボタンを押したときのコールバック。 +func _on_line_fill_button_button_down() -> void: + Main.layers.set_line_of_selected_layers(true) + Main.commit_history() + + +## 線の色のボタンを押したときのコールバック。 +func _on_line_color_button_button_down() -> void: + _line_popup.show() + + var pos := _line_button.get_screen_position() + Vector2(0, 44) + var popup_size := _line_popup.get_visible_rect().size + + # get safe area + var safe_area := DisplayServer.get_display_safe_area() + + # clamp pos + if pos.x + popup_size.x > safe_area.position.x + safe_area.size.x: + pos.x = safe_area.position.x + safe_area.size.x - popup_size.x + if pos.y + popup_size.y > safe_area.position.y + safe_area.size.y: + pos.y = safe_area.position.y + safe_area.size.y - popup_size.y + + _line_popup.position = pos + + +## 線のマテリアルを選択したときのコールバック。 +func _on_line_material_list_popup_on_material_selected(material_id: String) -> void: + Main.layers.set_line_material_of_selected_path_layers(material_id) + Main.commit_history() + + +## 線幅スライダーの値の変更で呼び出されるコールバック。 +func _on_h_slider_value_changed(value: float) -> void: + _line_width_spin_box.set_value_no_signal(value) + Main.layers.set_line_width_of_selected_path_layers(value) + _last_line_width_changed_time = Time.get_ticks_msec() + _last_line_width_changed_commit = false + + +## 線幅スピンボックスの値の変更で呼び出されるコールバック。 +func _on_spin_box_value_changed(value: float) -> void: + _line_width_slider.set_value_no_signal(value) + Main.layers.set_line_width_of_selected_path_layers(value) + _last_line_width_changed_time = Time.get_ticks_msec() + _last_line_width_changed_commit = false + + +## マテリアルの色のボタンを押したときのコールバック。 +func _on_fill_layer_color_button_button_down() -> void: + _fill_layer_popup.show() + + var pos := _fill_layer_button.get_screen_position() + Vector2(0, 44) + var popup_size := _fill_layer_popup.get_visible_rect().size + + # get safe area + var safe_area := DisplayServer.get_display_safe_area() + + # clamp pos + if pos.x + popup_size.x > safe_area.position.x + safe_area.size.x: + pos.x = safe_area.position.x + safe_area.size.x - popup_size.x + if pos.y + popup_size.y > safe_area.position.y + safe_area.size.y: + pos.y = safe_area.position.y + safe_area.size.y - popup_size.y + + _fill_layer_popup.position = pos + + +## 塗りつぶしの塗りマテリアルを設定したときのコールバック。 +func _on_material_list_popup_on_fill_layer_material_selected(material_id: String) -> void: + Main.layers.set_fill_material_of_selected_fill_layers(material_id) + Main.commit_history() diff --git a/godot/src/tab/material_list_tab.gd b/godot/src/tab/material_list_tab.gd new file mode 100644 index 0000000..b49a1af --- /dev/null +++ b/godot/src/tab/material_list_tab.gd @@ -0,0 +1,204 @@ +## マテリアルのリストのタブのクラス。 +## マテリアルのリストの並べ替えのDnDの処理を含む。 +class_name MaterialListTab +extends PanelContainer + + +@onready var _add_button: MenuButton = %AddButton +@onready var _delete_button: Button = %DeleteButton +@onready var _delete_popup: Popup = %DeleteButton/Popup +@onready var _container: VBoxContainer = %ListContainer +@onready var _dnd_cursor: ListDropCursor = %ListContainer/ListDropCursor + +## マテリアルのリストのノード。 +var _list: Array[MaterialListItem] = [] + +## リストアイテムのシーン。 +const _list_item: PackedScene = preload("res://scenes/node/material_list_item.tscn") + +## ドラッグ中のアイテム。 +var _drag_item: MaterialListItem = null + + +func _ready() -> void: + Main.on_change_material_parameters_changed.connect(_update_material_list) + Main.on_update_material_list_tab.connect(_update_material_list) + Main.on_document_open.connect(_update_material_list) + Main.on_document_close.connect(_update_material_list) + Main.on_document_reload.connect(_update_material_list) + + _add_button.get_popup().id_pressed.connect(_on_add_ppressed) + + +func _input(_event: InputEvent) -> void: + # can dropではないときドロップ先カーソルを非表示にする + _dnd_cursor.visible = false + + +func _process(_delta: float) -> void: + if Main.document_opened: + _add_button.disabled = false + _delete_button.disabled = false + else: + _add_button.disabled = true + _delete_button.disabled = true + + +func _notification(what: int) -> void: + match what: + NOTIFICATION_DRAG_END: + _drag_end() + + +func _get_drag_data(_at_position: Vector2) -> Variant: + # ドラッグ開始時にマウスオーバーしていたアイテムをドラッグする + var item: MaterialListItem = null + for index in _list.size(): + var node := _list[index] + var selected: bool = Main.materials.selected_index == index + if selected: + set_drag_preview(node.duplicate() as MaterialListItem) + item = node + item.set_dragging(true) + break + _drag_item = item + return item + + +func _can_drop_data(_at_position: Vector2, data: Variant) -> bool: + if data is MaterialListItem: + _dnd_cursor.visible = true + + # ドラッグ先を示すカーソルのindexを計算する + var cursor_index := _container.get_child_count() - 1 + for index in _list.size(): + var item := _list[index] + if item.mouse_over_top: + cursor_index = index + elif item.mouse_over_bottom: + cursor_index = index + 1 + + # ドラッグ先を示すカーソルを挿入する + for child in _container.get_children(): + _container.remove_child(child) + for index in _list.size(): + var item := _list[index] + if index == cursor_index: + _container.add_child(_dnd_cursor) + _container.add_child(item) + if cursor_index == _list.size(): + _container.add_child(_dnd_cursor) + + return true + + _dnd_cursor.visible = false + return false + + +## ドラッグが終了したときに呼び出す関数。 +func _drag_end() -> void: + # アイテムをドラッグ中だったら、マテリアルの並び替えを発火する + if _drag_item is MaterialListItem: + var node := _drag_item as MaterialListItem + node.set_dragging(false) + var hover_container := Rect2(Vector2.ZERO, _container.size).has_point(_container.get_local_mouse_position()) + var cursor_index := _list.size() + for index in _list.size(): + var item := _list[index] + if item.mouse_over_top: + cursor_index = index + elif item.mouse_over_bottom: + cursor_index = index + 1 + item.set_dragging(false) + if hover_container: + Main.materials.reorder_selected_material(cursor_index) + Main.commit_history() + + _drag_item = null + _dnd_cursor.visible = false + + +## マテリアルリストに変更があったときに呼び出されるコールバック。 +func _update_material_list() -> void: + if not Main.document_opened: + while _list.size() > 0: + _list[_list.size() - 1].queue_free() + _list.remove_at(_list.size() - 1) + return + + # Mainのmaterialsの個数と表示用nodeの個数を合わせる + while _list.size() > Main.materials.list.size(): + _list[_list.size() - 1].queue_free() + _list.remove_at(_list.size() - 1) + while _list.size() < Main.materials.list.size(): + var list_item := _list_item.instantiate() as MaterialListItem + list_item.on_selected.connect((func(index: int) -> void: + Main.materials.set_selected_index(index) + Main.layers.clear_selected_items() + ).bind(_list.size())) + _container.add_child(list_item) + _list.append(list_item) + + # パラメータを合わせる + for index in Main.materials.list.size(): + var item := Main.materials.list[index] + if item.material is ColorPaintMaterial: + var color_material := item.material as ColorPaintMaterial + var list_item := _list[index] + list_item.material_id = item.id + list_item.set_color(color_material.color) + list_item.set_material_name(item.name) + list_item.set_selected(Main.materials.selected_index == index) + elif item.material is LinearGradientPaintMaterial: + var linear_gradient_material := item.material as LinearGradientPaintMaterial + var list_item := _list[index] + list_item.material_id = item.id + list_item.set_linear_gradient(linear_gradient_material.gradient_texture) + list_item.set_material_name(item.name) + list_item.set_selected(Main.materials.selected_index == index) + elif item.material is RadialGradientPaintMaterial: + var radial_gradient_material := item.material as RadialGradientPaintMaterial + var list_item := _list[index] + list_item.material_id = item.id + list_item.set_radial_gradient(radial_gradient_material.gradient_texture) + list_item.set_material_name(item.name) + list_item.set_selected(Main.materials.selected_index == index) + + +## 色の操作中かどうか。 +func is_manipulate_color() -> bool: + for item in _list: + if item.is_manipulate_color(): + return true + return false + + +## Addのポップアップがクリックされたときに呼び出されるコールバック。 +func _on_add_ppressed(id: int) -> void: + if id == 0: + Main.materials.add_color_material() + Main.commit_history() + elif id == 1: + Main.materials.add_linear_gradient_material() + Main.commit_history() + elif id == 2: + Main.materials.add_radial_gradient_material() + Main.commit_history() + + +## Deleteボタンがクリックされたときに呼び出されるコールバック。 +func _on_delete_button_button_up() -> void: + if Main.materials.selected_index != -1: + _delete_popup.show() + + +## DeleteダイアログでOKを押したときに呼び出されるコールバック。 +func _on_ok_button_button_up() -> void: + Main.materials.delete_material() + _delete_popup.hide() + Main.commit_history() + + +## Deleteモーダルダイアログでキャンセルを押したときに呼び出されるコールバック。 +func _on_cancel_button_button_up() -> void: + _delete_popup.hide() diff --git a/godot/styles/flat_color_button.tres b/godot/styles/flat_color_button.tres new file mode 100644 index 0000000..21f5e2b --- /dev/null +++ b/godot/styles/flat_color_button.tres @@ -0,0 +1,8 @@ +[gd_resource type="StyleBoxFlat" format=3 uid="uid://cvvu0fweitcpr"] + +[resource] +bg_color = Color(0.4, 0.4, 0.4, 1) +corner_radius_top_left = 3 +corner_radius_top_right = 3 +corner_radius_bottom_right = 3 +corner_radius_bottom_left = 3 diff --git a/godot/translation/translation.csv b/godot/translation/translation.csv new file mode 100644 index 0000000..237db13 --- /dev/null +++ b/godot/translation/translation.csv @@ -0,0 +1,95 @@ +keys,ja,en +MENU_FILE,ファイル,File +MENU_EDIT,編集,Edit +MENU_TOOL,ツール,Tool +FILE_NEW_DOCUMENT,新規ドキュメント,New Document +FILE_OPEN_DOCUMENT,ドキュメントを開く,Open Document +FILE_SAVE,上書き保存,Save +FILE_SAVE_AS,別名で保存,Save As +FILE_CLOSE_DOCUMENT,ドキュメントを閉じる,Close Document +FILE_EXPORT_IMAGE,画像のエクスポート,Export Image +EDIT_UNDO,もとに戻す,Undo +EDIT_REDO,やり直し,Redo +EDIT_CHANGE_DOCUMENT_SIZE,ドキュメントサイズの変更,Change Document Size +TOOL_CONFIG,環境設定,Config +TOOL_LICENSES,ライセンス,Licenses +BLEND_MODE_NORMAL,通常,Normal +BLEND_MODE_ADD,加算,Add +BLEND_MODE_MULTIPLY,乗算,Multiply +BLEND_MODE_SCREEN,スクリーン,Screen +BLEND_MODE_OVERLAY,オーバーレイ,Overlay +BOOLEAN_UNION,結合,Union +BOOLEAN_DIFF,除外,Diff +BOOLEAN_INTERSECT,交差,Intersect +BOOLEAN_XOR,中マド,Xor +LAYER_SETTINGS_FILL,"塗り: ","Fill: " +LAYER_SETTINGS_LINE,"線: ","Line: " +LAYER_SETTINGS_LINE_WIDTH,"線幅: ","Line Width: " +MATERIAL_SOLID,単色,Solid Color +MATERIAL_LINEAR_GRADIENT,線形グラデーション,Linear Gradiation +MATERIAL_RADIAL_GRADIENT,放射グラデーション,Radial Gradiation +MATERIAL_CONFIRM_DELETE_TITLE,マテリアルの削除の確認,Confirmation of Material Deletion +MATERIAL_CONFIRM_DELETE_TEXT,マテリアルを削除します。,Delete material. +MATERIAL_CONFIRM_DELETE_OK,OK,Confirm +MATERIAL_CONFIRM_DELETE_CANCEL,キャンセル,Cacnel +CONFIG_TITLE,環境設定,Config +CONFIG_LANGUAGE,"言語設定: ","Language: " +CONFIG_CURRENT_LANGUAGE,日本語,English +CONFIG_ENABLE_AUTO_SAVE,"自動保存を有効にする: ","Enable Auto Save: " +CONFIG_AUTO_SAVE_INTERVAL,"自動保存の間隔 (秒): ","Auto save interval (seconds): " +CONFIG_CLOSE,閉じる,Close +EXPORT_TITLE,エクスポート,Export +EXPORT_WIDTH,"幅: ","Width: " +EXPORT_HEIGHT,"高さ: ","Height: " +EXPORT_EXPORT,エクスポート,Export +EXPORT_CANCEL,キャンセル,Cancel +NEW_DOCUMENT_TITLE,新規ドキュメント,New Document +NEW_DOCUMENT_SIZE_PRESET,サイズプリセット,Size Presets +NEW_DOCUMENT_WIDTH,"幅: ","Width: " +NEW_DOCUMENT_HEIGHT,"高さ: ","Height: " +NEW_DOCUMENT_CREATE,作成,Create +NEW_DOCUMENT_CANCEL,キャンセル,Cancel +DOCUMENT_SIZE_TITLE,ドキュメントサイズ変更,Change Document Size +DOCUMENT_SIZE_WIDTH,"幅: ","Width: " +DOCUMENT_SIZE_HEIGHT,"高さ: ","Height: " +DOCUMENT_SIZE_ANCHOR,"アンカー: ","Anchor: " +DOCUMENT_SIZE_CHANGE_SIZE,サイズ変更,Change Size +DOCUMENT_SIZE_CANCEL,キャンセル,Cancel +LINEAR_GRADIENT_START_POINT,"開始点: ","Start Point: " +LINEAR_GRADIENT_END_POINT,"終了点: ","End Point: " +LINEAR_GRADIENT_EDIT,編集,Edit +RADIAL_GRADIENT_CENTER_POINT,"中心点: ","Center Point: " +RADIAL_GRADIENT_HANDLE_1,"ハンドル1: ","Handle 1: " +RADIAL_GRADIENT_HANDLE_2,"ハンドル2: ","Handle 2: " +RADIAL_GRADIENT_EDIT,編集,Edit +TOOLTIP_DRAWING_TOOL,描画ツール,Drawing Tool +TOOLTIP_MANIPULATE_TOOL,操作ツール,Manipulate Tool +TOOLTIP_PARAMETER_TUNING_TOOL,調整ツール,Parameter Tuning Tool +TOOLTIP_EXPAND,枠内に収める,Expand +TOOLTIP_RESET_ZOOM,拡大率をリセット,Reset Zoom +TOOLTIP_MIRROR,左右反転,Mirror +TOOLTIP_BLEND_MODE,ブレンドモード,Blend Mode +TOOLTIP_ALPHA,透明度,Alpha +TOOLTIP_NEW_PATH_LAYER,新規パスレイヤーの追加,Add New Path Layer +TOOLTIP_NEW_FILL_LAYER,新規塗りつぶしレイヤーの追加,Add New Fill Layer +TOOLTIP_NEW_GROUP_LAYER,新規フォルダーレイヤーの追加,Add New Group Layer +TOOLTIP_DELETE_LAYER,レイヤーの削除,Delete Layer +TOOLTIP_CLIPPING,下のレイヤーでクリッピング,Clipping on the lower layer +TOOLTIP_LAYER_LOCK,レイヤーのロック,Layer Lock +TOOLTIP_PATH_OPEN_CLOSE,パスのOpen/Closed切り替え,Open/Closed path switching +TOOLTIP_BOOLEAN,パスのブーリアン演算,Boolean operations on paths +TOOLTIP_SHOW_FILL_MATERIAL,塗りのマテリアルを表示,Show fill material +TOOLTIP_HIDE_FILL_MATERIAL,塗りのマテリアルを非表示,Hide fill material +TOOLTIP_SELECT_FILL_MATERIAL,塗りのマテリアルの選択,Select fill material +TOOLTIP_SHOE_LINE_MATERIAL,線のマテリアルを表示,Show line material +TOOLTIP_HIDE_LINE_MATERIAL,線のマテリアルを非表示,Hide line material +TOOLTIP_SELECT_LINE_MATERIAL,線のマテリアルの選択,Select line material +TOOLTIP_LINE_WIDTH,線の幅,Line width +TOOLTIP_SELECT_FILL_LAYER_MATERIAL,塗りつぶしのマテリアルの選択,Select fill layer material +TOOLTIP_ADD_MATERIAL,マテリアルの追加,Add Material +TOOLTIP_DELETE_MATERIAL,マテリアルの削除,Delete Material +TOOLTIP_MATRERIAL_DELETED,マテリアルが削除されています。,The material is deleted. +LAYER,レイヤー,Layer +PATH,パス,Path +MATERIAL,マテリアル,Material +COPY,コピー,Copy diff --git a/godot/translation/translation.csv.import b/godot/translation/translation.csv.import new file mode 100644 index 0000000..b493cf4 --- /dev/null +++ b/godot/translation/translation.csv.import @@ -0,0 +1,17 @@ +[remap] + +importer="csv_translation" +type="Translation" +uid="uid://c66p26iuek1jd" + +[deps] + +files=["res://translation/translation.ja.translation", "res://translation/translation.en.translation"] + +source_file="res://translation/translation.csv" +dest_files=["res://translation/translation.ja.translation", "res://translation/translation.en.translation"] + +[params] + +compress=true +delimiter=0 diff --git a/godot/translation/translation.en.translation b/godot/translation/translation.en.translation new file mode 100644 index 0000000..cb37881 Binary files /dev/null and b/godot/translation/translation.en.translation differ diff --git a/godot/translation/translation.ja.translation b/godot/translation/translation.ja.translation new file mode 100644 index 0000000..b110a2c Binary files /dev/null and b/godot/translation/translation.ja.translation differ diff --git a/rust/.gitignore b/rust/.gitignore new file mode 100644 index 0000000..2f7896d --- /dev/null +++ b/rust/.gitignore @@ -0,0 +1 @@ +target/ diff --git a/rust/Cargo.lock b/rust/Cargo.lock new file mode 100644 index 0000000..0eed593 --- /dev/null +++ b/rust/Cargo.lock @@ -0,0 +1,387 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "gdextension-api" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3f2fc05d781fbc1da92d13480d464b2bf66b2e8cf230746f3a9ded3eb9bf6de" + +[[package]] +name = "gensym" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "913dce4c5f06c2ea40fc178c06f777ac89fc6b1383e90c254fafb1abe4ba3c82" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "uuid", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "glam" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e05e7e6723e3455f4818c7b26e855439f7546cf617ef669d1adedb8669e5cb9" + +[[package]] +name = "godot" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "246203d267e0523aa8877e7d5145fa34a5bbdb1a5c8e135f4dacbdeb074c1fc9" +dependencies = [ + "godot-core", + "godot-macros", +] + +[[package]] +name = "godot-bindings" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b664d294f99139f828b76b6e9955aa1b7455cc03fac08917b57a4578d22489e7" +dependencies = [ + "gdextension-api", +] + +[[package]] +name = "godot-cell" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28ee918b831ef1c455283b96d8527d0ddee556363c757fc18e01c75935bf7fb8" + +[[package]] +name = "godot-codegen" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51e6d5d3f836cdc4487de17f3422f7b25c3f95a11472132f8e8ef698dd907cd4" +dependencies = [ + "godot-bindings", + "heck", + "nanoserde", + "proc-macro2", + "quote", + "regex", +] + +[[package]] +name = "godot-core" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96dbdc7c65fb4a2dc58b66c2bb91f2b092f55afa42d2ffd509fa454f123f3e51" +dependencies = [ + "glam", + "godot-bindings", + "godot-cell", + "godot-codegen", + "godot-ffi", +] + +[[package]] +name = "godot-ffi" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da4a1f0d0c8c1443ba8732f011a532ae35793e276567e2fcd90a8ef9c09d90c9" +dependencies = [ + "gensym", + "godot-bindings", + "godot-codegen", + "libc", + "paste", +] + +[[package]] +name = "godot-macros" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff936aabcda6b8bcba69a4367938369e41bcdf17601f07ddda153947a481a0f0" +dependencies = [ + "godot-bindings", + "proc-macro2", + "quote", + "venial", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "i_float" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1cdb652ce4005fda3e653f98feae62a0f1727b3748e0339c3415dfafc178e8e" +dependencies = [ + "serde", +] + +[[package]] +name = "i_overlay" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d84163fb3751b7ce85b5a685e1d3a3b50ee3f4c00ed66632b63d856ac42f380b" +dependencies = [ + "i_float", + "i_shape", + "i_tree", + "rayon", +] + +[[package]] +name = "i_shape" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e0b30c19dda08d670c259f799d9ad7d28f0decc419494513c8adc1863171cc" +dependencies = [ + "i_float", + "serde", +] + +[[package]] +name = "i_tree" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cce4e0c28e92a783988b0177899cde7787a8049f4021d566d66c550e61cb8d48" + +[[package]] +name = "i_triangle" +version = "0.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31453640b7d520a01ef6e0025739d1623a087f3d4cda2cd02291c5f3d87ba49a" +dependencies = [ + "i_overlay", + "serde", +] + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "nanoserde" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5de9cf844ab1e25a0353525bd74cb889843a6215fa4a0d156fd446f4857a1b99" +dependencies = [ + "nanoserde-derive", +] + +[[package]] +name = "nanoserde-derive" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e943b2c21337b7e3ec6678500687cdc741b7639ad457f234693352075c082204" + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "regex" +version = "1.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + +[[package]] +name = "serde" +version = "1.0.204" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.204" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "shape" +version = "0.1.0" +dependencies = [ + "godot", + "i_float", + "i_overlay", + "i_shape", + "i_triangle", +] + +[[package]] +name = "syn" +version = "2.0.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "uuid" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5de17fd2f7da591098415cff336e12965a28061ddace43b59cb3c430179c9439" +dependencies = [ + "getrandom", +] + +[[package]] +name = "venial" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6816bc32f30bf8dd1b3adb04de8406c7bf187d2f923bd9e4c0b99365d012613f" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/rust/Cargo.toml b/rust/Cargo.toml new file mode 100644 index 0000000..a2f2ee6 --- /dev/null +++ b/rust/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "shape" +version = "0.1.0" +edition = "2021" +license = "MIT" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +godot = "0.1.3" +i_float = "1.3.0" +i_overlay = "1.5.2" +i_shape = "1.3.0" +i_triangle = "0.25.2" diff --git a/rust/src/lib.rs b/rust/src/lib.rs new file mode 100644 index 0000000..6701763 --- /dev/null +++ b/rust/src/lib.rs @@ -0,0 +1,45 @@ +use godot::engine::Engine; +use godot::prelude::*; + +mod ppw_curve; +mod ppw_path; +mod ppw_polygon; +mod shape_util; + +struct PPWPExtension; + +#[gdextension] +unsafe impl ExtensionLibrary for PPWPExtension { + fn on_level_init(level: InitLevel) { + if level == InitLevel::Scene { + Engine::singleton().register_singleton( + StringName::from("PPWCurve"), + ppw_curve::PPWCurve::new_alloc().upcast(), + ); + Engine::singleton().register_singleton( + StringName::from("ShapeUtil"), + shape_util::ShapeUtil::new_alloc().upcast(), + ); + } + } + + fn on_level_deinit(level: InitLevel) { + if level == InitLevel::Scene { + let mut engine = Engine::singleton(); + + let singleton_name = StringName::from("PPWCurve"); + let singleton = engine + .get_singleton(singleton_name.clone()) + .expect("cannot retrieve the singleton"); + engine.unregister_singleton(singleton_name); + singleton.free(); + + let singleton_name = StringName::from("ShapeUtil"); + let singleton = engine + .get_singleton(singleton_name.clone()) + .expect("cannot retrieve the singleton"); + engine.unregister_singleton(singleton_name); + singleton.free(); + } + } +} diff --git a/rust/src/ppw_curve.rs b/rust/src/ppw_curve.rs new file mode 100644 index 0000000..02ded06 --- /dev/null +++ b/rust/src/ppw_curve.rs @@ -0,0 +1,374 @@ +use godot::prelude::*; + +use crate::ppw_path::PPWPath; +use crate::ppw_polygon::PPWPolygon; + +#[derive(GodotClass)] +#[class(init, base=Object)] +pub struct PPWCurve { + base: Base, +} +#[godot_api] +impl PPWCurve { + const POINTS_PER_SEGMENT: usize = 50; + const PSI_INFINITY: f32 = 2.0; + const NEWTON_EPSILON: f64 = 0.001; + const MAX_NEWTON_ITERATION: usize = 300; + + fn calc_rational_bezier_point( + p0: Vector2, + p1: Vector2, + p2: Vector2, + w: f32, + t: f32, + ) -> Vector2 { + return ((1.0 - t) * (1.0 - t) * p0 + 2.0 * w * t * (1.0 - t) * p1 + t * t * p2) + / ((1.0 - t) * (1.0 - t) + 2.0 * w * t * (1.0 - t) + t * t); + } + + fn calc_max_curvature_p1(cp0: Vector2, cp1: Vector2, cp2: Vector2, w: f32) -> (Vector2, f32) { + // セグメントが潰れている場合0.5とする + if (cp0 - cp2).is_zero_approx() { + return (cp0, 0.5); + } + + // セグメントの端にCPがある場合も特殊扱い + if (cp1 - cp0).is_zero_approx() { + return (cp1, 0.0); + } + if (cp1 - cp2).is_zero_approx() { + return (cp1, 1.0); + } + + let w = w as f64; + let p = cp0 - cp1; + let r = cp2 - cp1; + let p_length = p.length() as f64; + let r_length = r.length() as f64; + let p_sqr_length = p_length * p_length; + let r_sqr_length = r_length * r_length; + let p_plus_r_sqr_length = (p + r).length_squared() as f64; + let p_minus_r_sqr_length = (p - r).length_squared() as f64; + let p_dot_r = p.dot(r) as f64; + let ww = w * w; + + let alpha_beta_to_q_t = |alpha: f64, beta: f64| -> (Vector2, f32) { + let l0 = (alpha + beta) / 2.0; + let l2 = (alpha - beta) / 2.0; + let l1 = 1.0 - alpha; + let q = (cp1 - l0 as f32 * cp0 - l2 as f32 * cp2) / l1 as f32; + let sqrt = if l0 * l2 < 0.0 { 0.0 } else { (l0 * l2).sqrt() }; + let t = (2.0 * w * l2 + l1) / (2.0 * w * (alpha + 2.0 * sqrt)); + (q, t as f32) + }; + + // 特殊解: cp1がcp0とcp2の垂直二等分線上にある場合 + if (p_sqr_length - r_sqr_length).abs() < 0.0001 { + return alpha_beta_to_q_t(1.0 / (1.0 + w) as f64, 0.0); + } + + // 特殊解: cp1がcp0とcp2を結んだ直線上にある場合 + if (p_dot_r.abs() - p_length * r_length).abs() < 0.0001 { + let m = (cp0 + cp2) / 2.0; + let sign = (r_length - p_length).sign(); + let k = sign * (cp0 - m).length() as f64 / (cp1 - m).length() as f64; + if p_dot_r < 0.0 { + let beta = sign / (1.0 - ww * (1.0 - k * k)).sqrt(); + let alpha = (beta * (beta + k)) / (1.0 + k * beta); + return alpha_beta_to_q_t(alpha as f64, beta as f64); + } else { + let dby = 1.0 + w * (1.0 - k * k).sqrt(); + let alpha = 1.0 / dby; + let beta = k / dby; + return alpha_beta_to_q_t(alpha as f64, beta as f64); + } + } + + // ニュートン法の初期値をヒューリスティックで決める + let (mut alpha, mut beta) = { + if w <= 1.0 { + let now_a = 1.0 / (1.0 + w as f64); + let tmp_b = + (p_sqr_length - r_sqr_length) * (1.0 - 2.0 * now_a) / 3.0 / p_plus_r_sqr_length; + let now_b = if tmp_b < -now_a { + -now_a + } else if tmp_b > now_a { + now_a + } else { + tmp_b + }; + (now_a, now_b) + } else { + let chord = 2.0 * r_length / (p_length + r_length) - 1.0; + let now_b = 2.0 / (1.0 + (std::f64::consts::E.powf(-2.0 * chord / w as f64))) - 1.0; + let now_a = 1.0 / (1.0 - ww) + + (ww / (1.0 - ww) / (1.0 - ww) - ww * now_b * now_b / (1.0 - ww)).sqrt(); + (now_a, now_b) + } + }; + + let f = |a: f64, b: f64| (1.0 - ww) * a * a - 2.0 * a + 1.0 + ww * b * b; + let g = |a: f64, b: f64| { + p_plus_r_sqr_length * b * b * b + - (p_sqr_length - r_sqr_length) * (1.0 - 2.0 * a) * b * b + + (p_minus_r_sqr_length * a - 2.0 * (p_sqr_length + r_sqr_length)) * a * b + - (p_sqr_length - r_sqr_length) * a * a + }; + let fda = |a: f64| 2.0 * (1.0 - ww) * a - 2.0; + let fdb = |b: f64| 2.0 * ww * b; + let gda = |a: f64, b: f64| { + 2.0 * (p_sqr_length - r_sqr_length) * b * b + 2.0 * b * p_minus_r_sqr_length * a + - 2.0 * b * (p_sqr_length + r_sqr_length) + - 2.0 * (p_sqr_length - r_sqr_length) * a + }; + let gdb = |a: f64, b: f64| { + 3.0 * p_plus_r_sqr_length * b * b + - 2.0 * (p_sqr_length - r_sqr_length) * (1.0 - 2.0 * a) * b + + (p_minus_r_sqr_length * a - 2.0 * (p_sqr_length + r_sqr_length)) * a + }; + + for _ in 0..Self::MAX_NEWTON_ITERATION { + let rf = f(alpha, beta); + let rg = g(alpha, beta); + if rf.abs() < Self::NEWTON_EPSILON && rg.abs() < Self::NEWTON_EPSILON { + break; + } + let rfda = fda(alpha); + let rfdb = fdb(beta); + let rgda = gda(alpha, beta); + let rgdb = gdb(alpha, beta); + let det = rfda * rgdb - rfdb * rgda; + alpha -= (rf * rgdb - rg * rfdb) / det; + beta -= (-rf * rgda + rg * rfda) / det; + } + + alpha_beta_to_q_t(alpha, beta) + } + + fn blend_coefficient(t: f32, phi: f32, psi: f32) -> (f32, f32) { + let ephi = std::f32::consts::E.powf(phi); + let sigma = 2.0 / (1.0 + ephi); + let delta = + -0.5 * (ephi - (ephi + 1.0).sqrt() * (ephi - 1.0).sqrt()).log(std::f32::consts::E); + let t2 = if psi <= -Self::PSI_INFINITY { + 0.0 + } else if psi >= Self::PSI_INFINITY { + 1.0 + } else { + t / (std::f32::consts::E.powf(-psi) * (1.0 - t) + t) + }; + let t3 = delta * (2.0 * t2 - 1.0); + let ht = t3.tanh() - sigma * t3; + let ha = delta.tanh() - sigma * delta; + let b1 = (1.0 - ht / ha) / 2.0; + let b2 = 1.0 - b1; + (b1, b2) + } + + fn calc_segment( + cp0: Vector2, + cp1: Vector2, + cp2: Vector2, + cp3: Vector2, + w1: f32, + w2: f32, + phi: f32, + psi: f32, + ) -> PackedVector2Array { + let mut curve1 = [Vector2::ZERO; Self::POINTS_PER_SEGMENT]; + let p0 = cp0; + let (p1, tt) = Self::calc_max_curvature_p1(cp0, cp1, cp2, w1); + let p2 = cp2; + for i in 0..Self::POINTS_PER_SEGMENT { + let t = tt + (1.0 - tt) * i as f32 / (Self::POINTS_PER_SEGMENT - 1) as f32; + let p = Self::calc_rational_bezier_point(p0, p1, p2, w1, t); + curve1[i] = p; + } + + let mut curve2 = [Vector2::ZERO; Self::POINTS_PER_SEGMENT]; + let p1 = cp1; + let (p2, tt) = Self::calc_max_curvature_p1(cp1, cp2, cp3, w2); + let p3 = cp3; + for i in 0..Self::POINTS_PER_SEGMENT { + let t = tt * i as f32 / (Self::POINTS_PER_SEGMENT - 1) as f32; + let p = Self::calc_rational_bezier_point(p1, p2, p3, w2, t); + curve2[i] = p; + } + + let mut segment = PackedVector2Array::new(); + for i in 0..Self::POINTS_PER_SEGMENT { + let t = i as f32 / (Self::POINTS_PER_SEGMENT - 1) as f32; + let (b1, b2) = Self::blend_coefficient(t, phi, psi); + let p = b1 * curve1[i] + b2 * curve2[i]; + segment.push(p); + } + segment + } + + fn calc_start_segment( + cp1: Vector2, + cp2: Vector2, + cp3: Vector2, + w2: f32, + phi: f32, + psi: f32, + ) -> PackedVector2Array { + let mut curve1 = [Vector2::ZERO; Self::POINTS_PER_SEGMENT]; + for i in 0..Self::POINTS_PER_SEGMENT { + let t = i as f32 / (Self::POINTS_PER_SEGMENT - 1) as f32; + let p = cp2 * t + (1.0 - t) * cp1; + curve1[i] = p; + } + + let mut curve2 = [Vector2::ZERO; Self::POINTS_PER_SEGMENT]; + let p1 = cp1; + let (p2, tt) = Self::calc_max_curvature_p1(cp1, cp2, cp3, w2); + let p3 = cp3; + for i in 0..Self::POINTS_PER_SEGMENT { + let t = tt * i as f32 / (Self::POINTS_PER_SEGMENT - 1) as f32; + let p = Self::calc_rational_bezier_point(p1, p2, p3, w2, t); + curve2[i] = p; + } + + let mut segment = PackedVector2Array::new(); + for i in 0..Self::POINTS_PER_SEGMENT { + let t = i as f32 / (Self::POINTS_PER_SEGMENT - 1) as f32; + let (b1, b2) = Self::blend_coefficient(t, phi, psi); + let p = b1 * curve1[i] + b2 * curve2[i]; + segment.push(p); + } + segment + } + + fn calc_end_segment( + cp0: Vector2, + cp1: Vector2, + cp2: Vector2, + w1: f32, + phi: f32, + psi: f32, + ) -> PackedVector2Array { + let mut curve1 = [Vector2::ZERO; Self::POINTS_PER_SEGMENT]; + let p0 = cp0; + let (p1, tt) = Self::calc_max_curvature_p1(cp0, cp1, cp2, w1); + let p2 = cp2; + for i in 0..Self::POINTS_PER_SEGMENT { + let t = tt + (1.0 - tt) * i as f32 / (Self::POINTS_PER_SEGMENT - 1) as f32; + let p = Self::calc_rational_bezier_point(p0, p1, p2, w1, t); + curve1[i] = p; + } + + let mut curve2 = [Vector2::ZERO; Self::POINTS_PER_SEGMENT]; + for i in 0..Self::POINTS_PER_SEGMENT { + let t = i as f32 / (Self::POINTS_PER_SEGMENT - 1) as f32; + let p = cp2 * t + (1.0 - t) * cp1; + curve2[i] = p; + } + + let mut segment = PackedVector2Array::new(); + for i in 0..Self::POINTS_PER_SEGMENT { + let t = i as f32 / (Self::POINTS_PER_SEGMENT - 1) as f32; + let (b1, b2) = Self::blend_coefficient(t, phi, psi); + let p = b1 * curve1[i] + b2 * curve2[i]; + segment.push(p); + } + segment + } + + #[func] + fn convert(&self, path: Gd) -> Gd { + let path = path.bind(); + + if !path.validate() { + godot_error!("Invalid path"); + return Gd::from_object(PPWPolygon::empty()); + } + + if path.get_control_points().len() == 1 { + return Gd::from_object(PPWPolygon::empty()); + } + + if path.get_control_points().len() == 2 { + let poly = PPWPolygon::empty(); + let mut polygon = poly.get_polygon(); + polygon.push(path.get_control_points()[0]); + polygon.push(path.get_control_points()[1]); + let mut segments = poly.get_segments(); + let mut segment = PackedVector2Array::new(); + segment.push(path.get_control_points()[0]); + segment.push(path.get_control_points()[1]); + segments.push(segment); + return Gd::from_object(poly); + } + + if path.get_is_closed() { + let mut segments = Array::new(); + let cp_len = path.get_control_points().len(); + for i in 0..cp_len { + let cp0 = path.get_control_points()[(i + cp_len - 1) % cp_len]; + let cp1 = path.get_control_points()[i]; + let cp2 = path.get_control_points()[(i + 1) % cp_len]; + let cp3 = path.get_control_points()[(i + 2) % cp_len]; + let w1 = path.get_weights()[i % cp_len]; + let w2 = path.get_weights()[(i + 1) % cp_len]; + let phi = path.get_phis()[i]; + let psi = path.get_psis()[i]; + let segment = Self::calc_segment(cp0, cp1, cp2, cp3, w1, w2, phi, psi); + segments.push(segment); + } + let mut polygon = PackedVector2Array::new(); + for i in 0..segments.len() { + let seg = segments.at(i); + for j in 0..seg.len() { + polygon.push(seg[j]); + } + } + let mut poly = PPWPolygon::empty(); + poly.set_polygon(polygon); + poly.set_segments(segments); + Gd::from_object(poly) + } else { + let mut segments = Array::new(); + let cp_len = path.get_control_points().len(); + segments.push(Self::calc_start_segment( + path.get_control_points()[0], + path.get_control_points()[1], + path.get_control_points()[2], + path.get_weights()[1], + path.get_phis()[0], + path.get_psis()[0], + )); + for i in 1..(cp_len - 2) { + let cp0 = path.get_control_points()[i - 1]; + let cp1 = path.get_control_points()[i]; + let cp2 = path.get_control_points()[i + 1]; + let cp3 = path.get_control_points()[i + 2]; + let w1 = path.get_weights()[i]; + let w2 = path.get_weights()[i + 1]; + let phi = path.get_phis()[i]; + let psi = path.get_psis()[i]; + let segment = Self::calc_segment(cp0, cp1, cp2, cp3, w1, w2, phi, psi); + segments.push(segment); + } + segments.push(Self::calc_end_segment( + path.get_control_points()[cp_len - 3], + path.get_control_points()[cp_len - 2], + path.get_control_points()[cp_len - 1], + path.get_weights()[cp_len - 2], + path.get_phis()[cp_len - 2], + path.get_psis()[cp_len - 2], + )); + let mut polygon = PackedVector2Array::new(); + for i in 0..segments.len() { + let seg = segments.at(i); + for j in 0..seg.len() { + polygon.push(seg[j]); + } + } + let mut poly = PPWPolygon::empty(); + poly.set_polygon(polygon); + poly.set_segments(segments); + Gd::from_object(poly) + } + } +} diff --git a/rust/src/ppw_path.rs b/rust/src/ppw_path.rs new file mode 100644 index 0000000..3d0f728 --- /dev/null +++ b/rust/src/ppw_path.rs @@ -0,0 +1,33 @@ +use godot::prelude::*; + +#[derive(GodotClass)] +#[class(init)] +pub struct PPWPath { + #[var] + is_closed: bool, + #[var] + control_points: PackedVector2Array, + #[var] + weights: PackedFloat32Array, + #[var] + phis: PackedFloat32Array, + #[var] + psis: PackedFloat32Array, +} +impl PPWPath { + pub fn validate(&self) -> bool { + let control_points_len = self.control_points.len(); + let weights_len = self.weights.len(); + let phis_len = self.phis.len(); + let psis_len = self.psis.len(); + if self.get_is_closed() { + control_points_len == weights_len + && control_points_len == phis_len + && control_points_len == psis_len + } else { + control_points_len == weights_len + && control_points_len == phis_len + 1 + && control_points_len == psis_len + 1 + } + } +} diff --git a/rust/src/ppw_polygon.rs b/rust/src/ppw_polygon.rs new file mode 100644 index 0000000..2de0120 --- /dev/null +++ b/rust/src/ppw_polygon.rs @@ -0,0 +1,18 @@ +use godot::prelude::*; + +#[derive(GodotClass)] +#[class(init)] +pub struct PPWPolygon { + #[var] + polygon: PackedVector2Array, + #[var] + segments: Array, +} +impl PPWPolygon { + pub fn empty() -> Self { + Self { + polygon: PackedVector2Array::new(), + segments: Array::new(), + } + } +} diff --git a/rust/src/shape_util.rs b/rust/src/shape_util.rs new file mode 100644 index 0000000..aa07e00 --- /dev/null +++ b/rust/src/shape_util.rs @@ -0,0 +1,133 @@ +use godot::prelude::*; +use i_float::f64_point::F64Point; +use i_overlay::core::fill_rule::FillRule; +use i_triangle::triangulation::float::FloatTriangulate; + +#[derive(GodotClass)] +#[class(init)] +pub struct BooleanPath { + #[var] + polygon: PackedVector2Array, + #[var] + operation: i64, +} + +#[derive(GodotClass)] +#[class(init)] +#[derive(Debug)] +pub struct TriangulateResult { + #[var] + vertices: PackedVector2Array, + #[var] + indices: PackedInt32Array, + #[var] + counter_lines: Array, +} +impl TriangulateResult { + pub fn empty() -> Self { + Self { + vertices: PackedVector2Array::new(), + indices: PackedInt32Array::new(), + counter_lines: Array::new(), + } + } +} + +#[derive(GodotClass)] +#[class(init, base=Object)] +pub struct ShapeUtil { + base: Base, +} +#[godot_api] +impl ShapeUtil { + #[func] + fn triangulate(polygon: PackedVector2Array) -> Gd { + let shape = vec![polygon + .as_slice() + .into_iter() + .map(|v| F64Point::new(v.x as f64, v.y as f64)) + .collect::>()]; + + let triangulate = shape.to_triangulation(Some(FillRule::EvenOdd), 0.0); + + let mut result = TriangulateResult::empty(); + result.vertices = PackedVector2Array::from_iter( + triangulate + .points + .iter() + .map(|v| Vector2::new(v.x as f32, v.y as f32)), + ); + result.indices = triangulate + .indices + .iter() + .map(|v| *v as i32) + .collect::(); + result.counter_lines = shape + .into_iter() + .map(|poly| { + PackedVector2Array::from_iter( + poly.iter().map(|v| Vector2::new(v.x as f32, v.y as f32)), + ) + }) + .collect::>(); + Gd::from_object(result) + } + + #[func] + fn triangulate_polygons_non_zero(polygons: Array) -> Gd { + let shape = polygons + .iter_shared() + .map(|polygon| { + polygon + .as_slice() + .into_iter() + .map(|v| F64Point::new(v.x as f64, v.y as f64)) + .collect::>() + }) + .collect::>(); + + let triangulate = shape.to_triangulation(Some(FillRule::NonZero), 0.0); + + let mut result = TriangulateResult::empty(); + result.vertices = PackedVector2Array::from_iter( + triangulate + .points + .iter() + .map(|v| Vector2::new(v.x as f32, v.y as f32)), + ); + result.indices = triangulate + .indices + .iter() + .map(|v| *v as i32) + .collect::(); + result.counter_lines = shape + .into_iter() + .map(|poly| { + PackedVector2Array::from_iter( + poly.iter().map(|v| Vector2::new(v.x as f32, v.y as f32)), + ) + }) + .collect::>(); + Gd::from_object(result) + } + + #[func] + fn distance_segment_and_point(segment: PackedVector2Array, point: Vector2) -> f32 { + let mut distance = f32::MAX; + for i in 0..(segment.len() - 1) { + let a = segment.get(i).unwrap(); + let b = segment.get(i + 1).unwrap(); + let ab = b - a; + let ap = point - a; + let bp = point - b; + if ap.dot(ab) < 0.0 { + distance = distance.min(ap.length()); + } else if bp.dot(ab) > 0.0 { + distance = distance.min(bp.length()); + } else { + distance = distance.min(ap.cross(ab).abs() / ab.length()); + } + } + return distance; + } +}