mirror of
https://github.com/JannisX11/blockbench.git
synced 2024-11-21 01:13:37 +08:00
Selection tool basics WIP
This commit is contained in:
parent
db83bdfc8e
commit
635ee169cd
@ -2019,6 +2019,38 @@ span.controller_state_section_info {
|
||||
font-size: 16px;
|
||||
padding-top: 2px;
|
||||
}
|
||||
#uv_selection_outline {
|
||||
--color-selection-outline-a: black;
|
||||
--color-selection-outline-b: white;
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
width: calc(100% + 2px);
|
||||
height: calc(100% + 2px);
|
||||
top: -1px;
|
||||
left: -1px;
|
||||
object-fit: cover;
|
||||
object-position: 0 0;
|
||||
}
|
||||
#uv_selection_outline path {
|
||||
fill: none;
|
||||
stroke-width: 2px;
|
||||
stroke: var(--color-selection-outline-a);
|
||||
}
|
||||
#uv_selection_outline path.dash_overlay {
|
||||
stroke: var(--color-selection-outline-b);
|
||||
stroke-dasharray: 6px 4px;
|
||||
stroke-dashoffset: 0;
|
||||
animation: selection-outline-shift 700ms linear infinite;
|
||||
animation-timing-function: steps(3, end);
|
||||
}
|
||||
@keyframes selection-outline-shift {
|
||||
0% {
|
||||
stroke-dashoffset: 0;
|
||||
}
|
||||
100% {
|
||||
stroke-dashoffset: -10px;
|
||||
}
|
||||
}
|
||||
|
||||
.face_properties_toggle {
|
||||
width: 32px;
|
||||
|
@ -374,13 +374,13 @@ class Action extends BarItem {
|
||||
return clone;
|
||||
}
|
||||
setIcon(icon) {
|
||||
var scope = this;
|
||||
this.icon = icon
|
||||
this.icon_node = Blockbench.getIconNode(this.icon)
|
||||
$(this.menu_node).find('.icon').replaceWith(this.icon_node)
|
||||
|
||||
this.nodes.forEach(function(n) {
|
||||
$(n).find('.icon').replaceWith($(scope.icon_node).clone())
|
||||
this.nodes.forEach(n => {
|
||||
let old_icon = n.querySelector('.icon:not(.action_more_options)');
|
||||
old_icon.replaceWith(this.icon_node.cloneNode(true));
|
||||
})
|
||||
}
|
||||
setName(name) {
|
||||
@ -2282,7 +2282,7 @@ const BARS = {
|
||||
'color_picker',
|
||||
'draw_shape_tool',
|
||||
'gradient_tool',
|
||||
'copy_paste_tool'
|
||||
'selection_tool',
|
||||
],
|
||||
vertical: Blockbench.isMobile == true,
|
||||
default_place: true
|
||||
@ -2385,6 +2385,7 @@ const BARS = {
|
||||
'copy_brush_mode',
|
||||
'draw_shape_type',
|
||||
'copy_paste_tool_mode',
|
||||
'selection_tool_operation_mode',
|
||||
'_',
|
||||
'slider_brush_size',
|
||||
'slider_brush_opacity',
|
||||
|
@ -623,9 +623,14 @@ window.addEventListener('blur', event => {
|
||||
delete Toolbox.original;
|
||||
}
|
||||
}
|
||||
let changed = Pressing.shift || Pressing.alt || Pressing.ctrl;
|
||||
let before = changed && {shift: Pressing.shift, alt: Pressing.alt, ctrl: Pressing.ctrl};
|
||||
Pressing.shift = false;
|
||||
Pressing.alt = false;
|
||||
Pressing.ctrl = false;
|
||||
if (changed) {
|
||||
Blockbench.dispatchEvent('update_pressed_modifier_keys', {before, now: Pressing});
|
||||
}
|
||||
})
|
||||
|
||||
window.addEventListener('focus', event => {
|
||||
@ -660,9 +665,17 @@ function getFocusedTextInput() {
|
||||
addEventListeners(document, 'keydown mousedown', function(e) {
|
||||
if (Keybinds.recording || e.which < 4) return;
|
||||
//Shift
|
||||
|
||||
|
||||
let modifiers_changed = Pressing.shift != e.shiftKey || Pressing.alt != e.altKey || Pressing.ctrl != e.ctrlKey;
|
||||
let before = modifiers_changed && {shift: Pressing.shift, alt: Pressing.alt, ctrl: Pressing.ctrl};
|
||||
Pressing.shift = e.shiftKey;
|
||||
Pressing.alt = e.altKey;
|
||||
Pressing.ctrl = e.ctrlKey;
|
||||
if (modifiers_changed) {
|
||||
Blockbench.dispatchEvent('update_pressed_modifier_keys', {before, now: Pressing});
|
||||
}
|
||||
|
||||
if (e.which === 16) {
|
||||
showShiftTooltip()
|
||||
}
|
||||
@ -863,7 +876,12 @@ $(document).keyup(function(e) {
|
||||
Toolbox.original.select()
|
||||
delete Toolbox.original;
|
||||
}
|
||||
let changed = Pressing.shift || Pressing.alt || Pressing.ctrl;
|
||||
let before = changed && {shift: Pressing.shift, alt: Pressing.alt, ctrl: Pressing.ctrl};
|
||||
Pressing.shift = false;
|
||||
Pressing.alt = false;
|
||||
Pressing.ctrl = false;
|
||||
if (changed) {
|
||||
Blockbench.dispatchEvent('update_pressed_modifier_keys', {before, now: Pressing});
|
||||
}
|
||||
})
|
||||
|
@ -215,8 +215,11 @@ function updateSelection(options = {}) {
|
||||
function selectAll() {
|
||||
if (Modes.animate) {
|
||||
selectAllKeyframes()
|
||||
} else if (Prop.active_panel == 'uv') {
|
||||
} else if (Prop.active_panel == 'uv' && Modes.edit) {
|
||||
UVEditor.selectAll()
|
||||
} else if (Prop.active_panel == 'uv' && Modes.paint && Texture.selected) {
|
||||
Texture.selected.texture_selection.setOverride(Texture.selected.texture_selection.override == true ? false : true);
|
||||
UVEditor.updateSelectionOutline();
|
||||
|
||||
} else if (Modes.edit && Mesh.selected.length && Mesh.selected.length === Outliner.selected.length && BarItems.selection_mode.value !== 'object') {
|
||||
let selection_mode = BarItems.selection_mode.value;
|
||||
|
@ -1299,10 +1299,14 @@ BARS.defineActions(function() {
|
||||
click() {
|
||||
if (Modes.animate) {
|
||||
unselectAllKeyframes()
|
||||
} else if (Prop.active_panel == 'uv') {
|
||||
} else if (Prop.active_panel == 'uv' && Modes.edit) {
|
||||
this.vue.selected_faces.empty();
|
||||
UVEditor.displayTools();
|
||||
|
||||
} else if (Prop.active_panel == 'uv' && Modes.paint) {
|
||||
Texture.selected.texture_selection.setOverride(false);
|
||||
UVEditor.updateSelectionOutline();
|
||||
|
||||
} else if (Modes.edit && Mesh.selected.length && Mesh.selected.length === Outliner.selected.length && BarItems.selection_mode.value !== 'object') {
|
||||
Mesh.selected.forEach(mesh => {
|
||||
delete Project.mesh_selection[mesh.uuid];
|
||||
|
@ -1636,6 +1636,91 @@ const Painter = {
|
||||
]
|
||||
}
|
||||
|
||||
class IntMatrix {
|
||||
constructor(width = 16, height = 16) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.array = null;
|
||||
this.override = false;
|
||||
}
|
||||
/**
|
||||
* The array does not exist by default to save memory, this activates it.
|
||||
*/
|
||||
activate() {
|
||||
this.array = new Int8Array(this.width * this.height);
|
||||
}
|
||||
/**
|
||||
* Get the value at the specified pixel
|
||||
* @param {*} x
|
||||
* @param {*} y
|
||||
* @returns
|
||||
*/
|
||||
get(x, y, simple_access) {
|
||||
if (this.override !== null) {
|
||||
return this.override
|
||||
} else {
|
||||
if (x < 0 || x >= this.width || y < 0 || y >= this.height) return 0;
|
||||
return this.array[y * this.width + x] || 0;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Set the value at a specified pixel
|
||||
* @param {number} x
|
||||
* @param {number} y
|
||||
* @param {number} value
|
||||
*/
|
||||
set(x, y, value) {
|
||||
if (this.override !== null) {
|
||||
if (!this.array) this.activate();
|
||||
if (this.override == true) {
|
||||
this.array.fill(1);
|
||||
}
|
||||
this.override = null;
|
||||
}
|
||||
this.array[y * this.width + x] = value;
|
||||
}
|
||||
/**
|
||||
* If there was a selection, whether override or not, clear it
|
||||
*/
|
||||
clear() {
|
||||
if (this.override == true) this.override = false;
|
||||
if (this.array) this.array.fill(0);
|
||||
}
|
||||
/**
|
||||
* Change override mode
|
||||
* @param {true|false|null} value
|
||||
* @returns
|
||||
*/
|
||||
setOverride(value) {
|
||||
if (value === this.override) return;
|
||||
this.override = value;
|
||||
if (value === null) {
|
||||
if (!this.array) {
|
||||
this.activate();
|
||||
} else {
|
||||
this.array.fill(0);
|
||||
}
|
||||
} else {
|
||||
delete this.array;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Change the size of the matrix. Unless using overrides, the selection gets lost.
|
||||
* @param {number} width
|
||||
* @param {number} height
|
||||
* @returns {boolean} Whether the size had to be changed
|
||||
*/
|
||||
changeSize(width, height) {
|
||||
if (width == this.width && height == this.height) return false;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
if (this.array) {
|
||||
this.array = new Int8Array(this.width * this.height);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
BARS.defineActions(function() {
|
||||
|
||||
new Tool('pan_tool', {
|
||||
@ -1993,7 +2078,7 @@ BARS.defineActions(function() {
|
||||
Interface.removeSuggestedModifierKey('shift', 'modifier_actions.snap_direction');
|
||||
}
|
||||
})
|
||||
new Tool('copy_paste_tool', {
|
||||
/*new Tool('copy_paste_tool', {
|
||||
icon: 'fa-vector-square',
|
||||
category: 'tools',
|
||||
toolbar: 'brush',
|
||||
@ -2016,7 +2101,52 @@ BARS.defineActions(function() {
|
||||
open_interface.confirm()
|
||||
}
|
||||
}
|
||||
})*/
|
||||
let selection_tool = new Tool('selection_tool', {
|
||||
icon: 'select',
|
||||
category: 'tools',
|
||||
toolbar: 'brush',
|
||||
cursor: 'crosshair',
|
||||
selectFace: true,
|
||||
transformerMode: 'hidden',
|
||||
paintTool: true,
|
||||
allowed_view_modes: ['textured'],
|
||||
modes: ['paint'],
|
||||
condition: {modes: ['paint']},
|
||||
side_menu: new Menu('selection_tool', () => {
|
||||
let modes = {
|
||||
rectangle: {icon: 'select'},
|
||||
ellipse: {icon: 'lasso_select'},
|
||||
lasso: {icon: 'fa-draw-polygon'},
|
||||
wand: {icon: 'fa-magic'},
|
||||
same_color: {icon: 'format_paint'},
|
||||
};
|
||||
let entries = [];
|
||||
for (let id in modes) {
|
||||
let entry = {
|
||||
id,
|
||||
name: id,
|
||||
icon: modes[id].icon,
|
||||
click() {
|
||||
selection_tool.setIcon(modes[id].icon);
|
||||
selection_tool.mode = id;
|
||||
selection_tool.select();
|
||||
}
|
||||
}
|
||||
entries.push(entry);
|
||||
}
|
||||
return entries;
|
||||
}),
|
||||
onCanvasClick(data) {
|
||||
if (data && data.element) {
|
||||
Blockbench.showQuickMessage('message.copy_paste_tool_viewport')
|
||||
}
|
||||
},
|
||||
onSelect() {
|
||||
|
||||
}
|
||||
})
|
||||
selection_tool.mode = 'rectangle';
|
||||
|
||||
new BarSelect('brush_shape', {
|
||||
category: 'paint',
|
||||
@ -2094,6 +2224,28 @@ BARS.defineActions(function() {
|
||||
move: true,
|
||||
}
|
||||
})
|
||||
new BarSelect('selection_tool_operation_mode', {
|
||||
category: 'paint',
|
||||
condition: {tools: ['selection_tool']},
|
||||
icon_mode: true,
|
||||
options: {
|
||||
create: {name: true, icon: 'shadow'},
|
||||
add: {name: true, icon: 'shadow_add'},
|
||||
subtract: {name: true, icon: 'shadow_minus'},
|
||||
intersect: {name: true, icon: 'join_inner'},
|
||||
}
|
||||
})
|
||||
Blockbench.on('update_pressed_modifier_keys', ({before, now}) => {
|
||||
let tool = BarItems.selection_tool_operation_mode;
|
||||
if (!Condition(tool.condition)) return;
|
||||
if (now.shift) {
|
||||
tool.set('add');
|
||||
} else if (now.ctrl) {
|
||||
tool.set('subtract');
|
||||
} else if (before.ctrl || before.shift) {
|
||||
tool.set('create');
|
||||
}
|
||||
});
|
||||
|
||||
StateMemory.init('mirror_painting_options', 'object');
|
||||
Painter.mirror_painting_options = StateMemory.mirror_painting_options;
|
||||
|
@ -26,6 +26,7 @@ class Texture {
|
||||
this.layers = [];
|
||||
this.layers_enabled = false;
|
||||
this.selected_layer = null;
|
||||
this.texture_selection = new IntMatrix(0, 0);
|
||||
this.internal = !isApp;
|
||||
this.uuid = uuid || guid()
|
||||
|
||||
@ -198,6 +199,7 @@ class Texture {
|
||||
let dimensions_changed = scope.width !== img.naturalWidth || scope.height !== img.naturalHeight;
|
||||
scope.width = img.naturalWidth;
|
||||
scope.height = img.naturalHeight;
|
||||
scope.texture_selection.changeSize(scope.width, scope.height);
|
||||
if (img.naturalWidth > 16384 || img.naturalHeight > 16384) {
|
||||
scope.error = 2;
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ const UVEditor = {
|
||||
];
|
||||
}
|
||||
|
||||
if (Toolbox.selected.id === 'copy_paste_tool') {
|
||||
if (Toolbox.selected.id === 'copy_paste_tool' || Toolbox.selected.id === 'selection_tool') {
|
||||
if (settings.nearest_rectangle_select.value) {
|
||||
result.x = Math.round(mouse_coords[0]/pixel_size*1);
|
||||
result.y = Math.round(mouse_coords[1]/pixel_size*1);
|
||||
@ -65,13 +65,13 @@ const UVEditor = {
|
||||
if (texture) {
|
||||
var coords = this.getBrushCoordinates(event, texture)
|
||||
|
||||
if (Toolbox.selected.id !== 'copy_paste_tool') {
|
||||
Painter.startPaintTool(texture, coords.x, coords.y, undefined, event)
|
||||
if (Toolbox.selected.id == 'selection_tool') {
|
||||
this.vue.startTextureSelection(coords.x, coords.y, event);
|
||||
} else {
|
||||
this.startSelection(coords.x, coords.y, event)
|
||||
Painter.startPaintTool(texture, coords.x, coords.y, undefined, event);
|
||||
}
|
||||
}
|
||||
if (Toolbox.selected.id !== 'color_picker' && Toolbox.selected.id !== 'copy_paste_tool' && texture) {
|
||||
if (Toolbox.selected.id !== 'color_picker' && Toolbox.selected.id !== 'copy_paste_tool' && Toolbox.selected.id !== 'selection_tool' && texture) {
|
||||
addEventListeners(this.vue.$refs.frame, 'mousemove touchmove', UVEditor.movePaintTool, false );
|
||||
addEventListeners(document, 'mouseup touchend', UVEditor.stopBrush, false );
|
||||
}
|
||||
@ -111,121 +111,68 @@ const UVEditor = {
|
||||
UVEditor.stopSelection()
|
||||
}
|
||||
},
|
||||
// Copy Paste Tool
|
||||
startSelection(x, y, event) {
|
||||
if (Painter.selection.overlay && event.target && event.target.id === 'uv_frame' && !event.altKey) {
|
||||
if (open_interface) {
|
||||
open_interface.confirm()
|
||||
} else {
|
||||
this.removePastingOverlay()
|
||||
|
||||
selection_outline_lines: [],
|
||||
updateSelectionOutline(recalculate_lines = true) {
|
||||
let {texture} = this.vue;
|
||||
if (!texture) {
|
||||
this.vue.selection_outline = '';
|
||||
return;
|
||||
}
|
||||
if (texture.texture_selection.override == false) {
|
||||
this.vue.selection_outline = '';
|
||||
return;
|
||||
}
|
||||
let size = UVEditor.getPixelSize();
|
||||
let width = this.vue.texture.width;
|
||||
let height = this.vue.texture.display_height;
|
||||
|
||||
if (texture.texture_selection.override == true) {
|
||||
this.vue.selection_outline = `M1 1 L${width * size + 1} 1 L${width * size + 1} ${height * size + 1} L1 ${height * size + 1} L1 1`;
|
||||
return;
|
||||
}
|
||||
|
||||
let lines = UVEditor.selection_outline_lines;
|
||||
|
||||
if (recalculate_lines) {
|
||||
lines.empty();
|
||||
let matrix = texture.texture_selection;
|
||||
// =
|
||||
for (let y = 0; y <= height; y++) {
|
||||
let pre = null;
|
||||
for (let x = 0; x <= width; x++) {
|
||||
let a = matrix.get(x, y-1);
|
||||
let b = matrix.get(x, y);
|
||||
let line = a != b;
|
||||
if (pre !== line) {
|
||||
if (pre !== null || line) {
|
||||
lines.push([!!line, x, y]);
|
||||
}
|
||||
pre = line;
|
||||
}
|
||||
}
|
||||
}
|
||||
// ||
|
||||
for (let x = 0; x <= width; x++) {
|
||||
let pre = null;
|
||||
for (let y = 0; y <= height; y++) {
|
||||
let a = matrix.get(x-1, y);
|
||||
let b = matrix.get(x, y);
|
||||
let line = a != b;
|
||||
if (pre !== line) {
|
||||
if (pre !== null || line) {
|
||||
lines.push([!!line, x, y]);
|
||||
}
|
||||
pre = line;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
delete Painter.selection.calcrect;
|
||||
let viewport = UVEditor.vue.$refs.viewport;
|
||||
if (!Painter.selection.overlay) {
|
||||
$(this.vue.$refs.frame).find('#texture_selection_rect').detach();
|
||||
let rect = document.createElement('div');
|
||||
rect.style.visibility = 'hidden';
|
||||
rect.id = 'texture_selection_rect';
|
||||
this.vue.$refs.frame.append(rect)
|
||||
Painter.selection.rect = rect;
|
||||
Painter.selection.start_x = Math.clamp(x, 0, UVEditor.texture ? UVEditor.texture.width : Project.texture_width);
|
||||
Painter.selection.start_y = Math.clamp(y, 0, UVEditor.texture ? UVEditor.texture.height : Project.texture_height);
|
||||
UVEditor.vue.copy_overlay.width = 0;
|
||||
UVEditor.vue.copy_overlay.height = 0;
|
||||
} else {
|
||||
Painter.selection.start_x = Painter.selection.x;
|
||||
Painter.selection.start_y = Painter.selection.y;
|
||||
Painter.selection.start_scroll_x = viewport.scrollLeft;
|
||||
Painter.selection.start_scroll_y = viewport.scrollTop;
|
||||
Painter.selection.start_event = event;
|
||||
let outline = '';
|
||||
for (let line of lines) {
|
||||
outline += `${outline ? '' : ' '}${line[0] ? 'M' : 'L'}${line[1] * size + 1} ${line[2] * size + 1}`;
|
||||
}
|
||||
|
||||
function drag(e1) {
|
||||
let texture = UVEditor.texture;
|
||||
var {x, y} = UVEditor.getBrushCoordinates(e1, texture);
|
||||
if (texture.img.naturalWidth + texture.img.naturalHeight == 0) return;
|
||||
UVEditor.dragSelection(x, y, e1);
|
||||
}
|
||||
function stop() {
|
||||
removeEventListeners(document, 'pointermove', drag);
|
||||
removeEventListeners(document, 'pointerup', stop);
|
||||
UVEditor.stopSelection();
|
||||
}
|
||||
addEventListeners(document, 'pointermove', drag);
|
||||
addEventListeners(document, 'pointerup', stop);
|
||||
},
|
||||
dragSelection(x, y, event) {
|
||||
let m = UVEditor.inner_width / UVEditor.texture.width;
|
||||
|
||||
if (!Painter.selection.overlay) {
|
||||
let {start_x, start_y} = Painter.selection;
|
||||
if (!settings.nearest_rectangle_select.value) {
|
||||
if (x >= Painter.selection.start_x) x++;
|
||||
if (y >= Painter.selection.start_y) y++;
|
||||
if (x < Painter.selection.start_x) start_x++;
|
||||
if (y < Painter.selection.start_y) start_y++;
|
||||
}
|
||||
if (x === Painter.current.x && y === Painter.current.y) return;
|
||||
Painter.current.x = x = Math.clamp(x, 0, UVEditor.texture.img.naturalWidth);
|
||||
Painter.current.y = y = Math.clamp(y, 0, UVEditor.texture.img.naturalHeight);
|
||||
|
||||
let calcrect = getRectangle(start_x, start_y, x, y)
|
||||
if (!calcrect.x && !calcrect.y) return;
|
||||
UVEditor.vue.copy_overlay.state = 'select';
|
||||
Painter.selection.calcrect = calcrect;
|
||||
Painter.selection.x = calcrect.ax;
|
||||
Painter.selection.y = calcrect.ay;
|
||||
UVEditor.vue.copy_overlay.width = calcrect.x;
|
||||
UVEditor.vue.copy_overlay.height = calcrect.y;
|
||||
$(Painter.selection.rect)
|
||||
.css('left', calcrect.ax*m + 'px')
|
||||
.css('top', (calcrect.ay%UVEditor.texture.display_height)*m + 'px')
|
||||
.css('width', calcrect.x *m + 'px')
|
||||
.css('height', calcrect.y *m + 'px')
|
||||
.css('visibility', 'visible')
|
||||
|
||||
} else if (UVEditor.texture && Painter.selection.canvas) {
|
||||
let viewport = UVEditor.vue.$refs.viewport;
|
||||
let move_offset_x = event.clientX - Painter.selection.start_event.clientX - Painter.selection.start_scroll_x + viewport.scrollLeft;
|
||||
let move_offset_y = event.clientY - Painter.selection.start_event.clientY - Painter.selection.start_scroll_y + viewport.scrollTop;
|
||||
Painter.selection.x = Painter.selection.start_x + Math.round((move_offset_x) / m);
|
||||
Painter.selection.y = Painter.selection.start_y + Math.round((move_offset_y) / m);
|
||||
Painter.selection.x = Math.clamp(Painter.selection.x, 1-Painter.selection.canvas.width, UVEditor.texture.width -1)
|
||||
Painter.selection.y = Math.clamp(Painter.selection.y, 1-Painter.selection.canvas.height, UVEditor.texture.height-1)
|
||||
UVEditor.updatePastingOverlay()
|
||||
}
|
||||
},
|
||||
stopSelection() {
|
||||
if (Painter.selection.rect) {
|
||||
Painter.selection.rect.remove()
|
||||
}
|
||||
if (Painter.selection.overlay || !Painter.selection.calcrect) return;
|
||||
UVEditor.vue.copy_overlay.state = 'off';
|
||||
if (Painter.selection.calcrect.x == 0 || Painter.selection.calcrect.y == 0) return;
|
||||
|
||||
let calcrect = Painter.selection.calcrect;
|
||||
var canvas = document.createElement('canvas')
|
||||
var ctx = canvas.getContext('2d');
|
||||
canvas.width = calcrect.x;
|
||||
canvas.height = calcrect.y;
|
||||
ctx.drawImage(UVEditor.vue.texture.img, -calcrect.ax, -calcrect.ay)
|
||||
|
||||
if (isApp) {
|
||||
let image = nativeImage.createFromDataURL(canvas.toDataURL())
|
||||
clipboard.writeImage(image)
|
||||
}
|
||||
Painter.selection.canvas = canvas;
|
||||
|
||||
Painter.selection.move_mode = BarItems.copy_paste_tool_mode.value == 'move';
|
||||
if (Painter.selection.move_mode) {
|
||||
UVEditor.texture.edit((canvas) => {
|
||||
var ctx = canvas.getContext('2d');
|
||||
ctx.clearRect(Painter.selection.x, Painter.selection.y, Painter.selection.canvas.width, Painter.selection.canvas.height);
|
||||
}, {no_undo_finish: true});
|
||||
}
|
||||
|
||||
UVEditor.addPastingOverlay();
|
||||
this.vue.selection_outline = outline;
|
||||
},
|
||||
addPastingOverlay() {
|
||||
if (Painter.selection.overlay) return;
|
||||
@ -424,6 +371,7 @@ const UVEditor = {
|
||||
this.vue.zoom = zoom;
|
||||
Project.uv_viewport.zoom = this.zoom;
|
||||
Vue.nextTick(() => {
|
||||
UVEditor.updateSelectionOutline(false);
|
||||
if (Painter.selection.overlay) UVEditor.updatePastingOverlay()
|
||||
})
|
||||
return this;
|
||||
@ -2037,6 +1985,13 @@ Interface.definePanels(function() {
|
||||
height: 0,
|
||||
active: false
|
||||
},
|
||||
texture_selection_rect: {
|
||||
pos_x: 0,
|
||||
pos_y: 0,
|
||||
width: 0,
|
||||
height: 0,
|
||||
active: false
|
||||
},
|
||||
copy_overlay,
|
||||
|
||||
uv_resolution: [16, 16],
|
||||
@ -2044,6 +1999,7 @@ Interface.definePanels(function() {
|
||||
all_elements: [],
|
||||
selected_faces: [],
|
||||
display_uv: 'selected_elements',
|
||||
selection_outline: '',
|
||||
|
||||
face_names: {
|
||||
north: tl('face.north'),
|
||||
@ -2213,6 +2169,7 @@ Interface.definePanels(function() {
|
||||
this.texture = 0;
|
||||
}
|
||||
// Display canvas while painting
|
||||
UVEditor.updateSelectionOutline();
|
||||
this.updateTextureCanvas();
|
||||
},
|
||||
updateTextureCanvas() {
|
||||
@ -3261,6 +3218,176 @@ Interface.definePanels(function() {
|
||||
return style ? `${margin[1]}px ${margin[0]}px` : margin;
|
||||
},
|
||||
|
||||
startTextureSelection(x, y, event) {
|
||||
let texture = UVEditor.texture;
|
||||
if (!texture) return;
|
||||
if (texture.img.naturalWidth + texture.img.naturalHeight == 0) return;
|
||||
let clicked_val = texture.texture_selection.get(Math.floor(x), Math.floor(y));
|
||||
let selection_mode = BarItems.selection_tool.mode;
|
||||
let op_mode = BarItems.selection_tool_operation_mode.value;
|
||||
let selection_rect = this.texture_selection_rect;
|
||||
let start_x, start_y, calcrect;
|
||||
let create_selection = !(op_mode == 'create' && clicked_val);
|
||||
|
||||
/*if (op_mode == 'create' && clicked_val) {
|
||||
if (open_interface) {
|
||||
open_interface.confirm()
|
||||
} else {
|
||||
UVEditor.removePastingOverlay()
|
||||
}
|
||||
}*/
|
||||
let viewport = this.$refs.viewport;
|
||||
if (create_selection) {
|
||||
//$(this.$refs.frame).find('#texture_selection_rect').detach();
|
||||
//let rect = document.createElement('div');
|
||||
//rect.style.visibility = 'hidden';
|
||||
//rect.id = 'texture_selection_rect';
|
||||
//this.$refs.frame.append(rect)
|
||||
start_x = Math.clamp(x, 0, UVEditor.texture ? UVEditor.texture.width : Project.texture_width);
|
||||
start_y = Math.clamp(y, 0, UVEditor.texture ? UVEditor.texture.height : Project.texture_height);
|
||||
} else {
|
||||
//Painter.selection.start_x = Painter.selection.x;
|
||||
//Painter.selection.start_y = Painter.selection.y;
|
||||
//Painter.selection.start_scroll_x = viewport.scrollLeft;
|
||||
//Painter.selection.start_scroll_y = viewport.scrollTop;
|
||||
//Painter.selection.start_event = event;
|
||||
}
|
||||
|
||||
let last_x, last_y;
|
||||
function drag(e1) {
|
||||
|
||||
var {x, y} = UVEditor.getBrushCoordinates(e1, texture);
|
||||
if (last_x == x && last_y == y) return;
|
||||
last_x = x, last_y = y;
|
||||
|
||||
/*let rect = getRectangle(
|
||||
event.offsetX / scope.inner_width * scope.uv_resolution[0],
|
||||
event.offsetY / scope.inner_height * scope.uv_resolution[1],
|
||||
(event.offsetX - event.clientX + e1.clientX) / scope.inner_width * scope.uv_resolution[0],
|
||||
(event.offsetY - event.clientY + e1.clientY) / scope.inner_height * scope.uv_resolution[1],
|
||||
)
|
||||
selection_rect.pos_x = rect.ax;
|
||||
selection_rect.pos_y = rect.ay;
|
||||
selection_rect.width = rect.x;
|
||||
selection_rect.height = rect.y;*/
|
||||
|
||||
|
||||
if (create_selection) {
|
||||
let start_x_here = start_x;
|
||||
let start_y_here = start_y;
|
||||
if (!settings.nearest_rectangle_select.value) {
|
||||
if (x >= start_x) x++;
|
||||
if (y >= start_y) y++;
|
||||
if (x < start_x) start_x_here++;
|
||||
if (y < start_y) start_y_here++;
|
||||
}
|
||||
if (x === Painter.current.x && y === Painter.current.y) return;
|
||||
Painter.current.x = x = Math.clamp(x, 0, UVEditor.texture.img.naturalWidth);
|
||||
Painter.current.y = y = Math.clamp(y, 0, UVEditor.texture.img.naturalHeight);
|
||||
|
||||
calcrect = getRectangle(start_x_here, start_y_here, x, y);
|
||||
if (!calcrect.x && !calcrect.y) return;
|
||||
//UVEditor.vue.copy_overlay.state = 'select';
|
||||
//Painter.selection.calcrect = calcrect;
|
||||
//Painter.selection.x = calcrect.ax;
|
||||
//Painter.selection.y = calcrect.ay;
|
||||
//UVEditor.vue.copy_overlay.width = calcrect.x;
|
||||
//UVEditor.vue.copy_overlay.height = calcrect.y;
|
||||
|
||||
selection_rect.active = true;
|
||||
selection_rect.pos_x = calcrect.ax;
|
||||
selection_rect.pos_y = calcrect.ay;
|
||||
selection_rect.width = calcrect.x;
|
||||
selection_rect.height = calcrect.y;
|
||||
|
||||
} else {
|
||||
//let viewport = UVEditor.vue.$refs.viewport;
|
||||
//let move_offset_x = e1.clientX - Painter.selection.start_event.clientX - Painter.selection.start_scroll_x + viewport.scrollLeft;
|
||||
//let move_offset_y = e1.clientY - Painter.selection.start_event.clientY - Painter.selection.start_scroll_y + viewport.scrollTop;
|
||||
//Painter.selection.x = Painter.selection.start_x + Math.round((move_offset_x) / m);
|
||||
//Painter.selection.y = Painter.selection.start_y + Math.round((move_offset_y) / m);
|
||||
//Painter.selection.x = Math.clamp(Painter.selection.x, 1-Painter.selection.canvas.width, UVEditor.texture.width -1)
|
||||
//Painter.selection.y = Math.clamp(Painter.selection.y, 1-Painter.selection.canvas.height, UVEditor.texture.height-1)
|
||||
//UVEditor.updatePastingOverlay()
|
||||
}
|
||||
}
|
||||
function stop() {
|
||||
removeEventListeners(document, 'pointermove', drag);
|
||||
removeEventListeners(document, 'pointerup', stop);
|
||||
selection_rect.active = false;
|
||||
|
||||
if (create_selection) {
|
||||
if (selection_rect.width == 0 || selection_rect.height == 0) return;
|
||||
|
||||
if (op_mode == 'create') {
|
||||
texture.texture_selection.clear();
|
||||
}
|
||||
if (selection_mode == 'rectangle') {
|
||||
for (let x = calcrect.ax; x < calcrect.bx; x++) {
|
||||
for (let y = calcrect.ay; y < calcrect.by; y++) {
|
||||
switch (op_mode) {
|
||||
case 'create': case 'add': {
|
||||
texture.texture_selection.set(x, y, 1);
|
||||
break;
|
||||
}
|
||||
case 'subtract': {
|
||||
texture.texture_selection.set(x, y, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (selection_mode == 'ellipse') {
|
||||
let center_x = calcrect.ax + calcrect.x/2;
|
||||
let center_y = calcrect.ay + calcrect.y/2;
|
||||
for (let x = calcrect.ax; x < calcrect.bx; x++) {
|
||||
for (let y = calcrect.ay; y < calcrect.by; y++) {
|
||||
let distance = Math.sqrt(Math.pow((center_x - x - 0.5) / calcrect.x, 2) + Math.pow((center_y - y - 0.5) / calcrect.y, 2));
|
||||
if (distance > 0.5) continue;
|
||||
switch (op_mode) {
|
||||
case 'create': case 'add': {
|
||||
texture.texture_selection.set(x, y, 1);
|
||||
break;
|
||||
}
|
||||
case 'subtract': {
|
||||
texture.texture_selection.set(x, y, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
UVEditor.updateSelectionOutline();
|
||||
}
|
||||
|
||||
/*let calcrect = Painter.selection.calcrect;
|
||||
var canvas = document.createElement('canvas')
|
||||
var ctx = canvas.getContext('2d');
|
||||
canvas.width = calcrect.x;
|
||||
canvas.height = calcrect.y;
|
||||
ctx.drawImage(UVEditor.vue.texture.img, -calcrect.ax, -calcrect.ay)
|
||||
|
||||
/*if (isApp) {
|
||||
let image = nativeImage.createFromDataURL(canvas.toDataURL())
|
||||
clipboard.writeImage(image)
|
||||
}*/
|
||||
/*Painter.selection.canvas = canvas;
|
||||
|
||||
Painter.selection.move_mode = BarItems.copy_paste_tool_mode.value == 'move';
|
||||
if (Painter.selection.move_mode) {
|
||||
UVEditor.texture.edit((canvas) => {
|
||||
var ctx = canvas.getContext('2d');
|
||||
ctx.clearRect(Painter.selection.x, Painter.selection.y, Painter.selection.canvas.width, Painter.selection.canvas.height);
|
||||
}, {no_undo_finish: true});
|
||||
}
|
||||
|
||||
//UVEditor.addPastingOverlay();*/
|
||||
}
|
||||
addEventListeners(document, 'pointermove', drag);
|
||||
addEventListeners(document, 'pointerup', stop);
|
||||
},
|
||||
|
||||
checkFormat(values) {
|
||||
for (let key in values) {
|
||||
if (Format[key] != values[key]) return false;
|
||||
@ -3559,6 +3686,7 @@ Interface.definePanels(function() {
|
||||
}">
|
||||
</div>
|
||||
|
||||
|
||||
<div v-if="helper_lines.x >= 0" class="uv_helper_line_x" :style="{left: toPixels(helper_lines.x)}"></div>
|
||||
<div v-if="helper_lines.y >= 0" class="uv_helper_line_y" :style="{top: toPixels(helper_lines.y)}"></div>
|
||||
|
||||
@ -3573,10 +3701,26 @@ Interface.definePanels(function() {
|
||||
>
|
||||
<div ref="texture_canvas_wrapper" id="texture_canvas_wrapper" v-if="texture && texture.error != 1 && texture.display_canvas"></div>
|
||||
<img style="object-fit: fill; opacity: 0.02; mix-blend-mode: screen;" v-if="texture == 0 && !box_uv" src="./assets/missing_blend.png">
|
||||
|
||||
<svg id="uv_texture_grid" v-if="pixel_grid && mode == 'paint' && texture && texture.width">
|
||||
<path :d="textureGrid" />
|
||||
<path :d="textureGridBold" class="bold_grid" />
|
||||
</svg>
|
||||
|
||||
<div id="texture_selection_rect"
|
||||
v-if="texture_selection_rect.active"
|
||||
:style="{
|
||||
left: toPixels(texture_selection_rect.pos_x),
|
||||
top: toPixels(texture_selection_rect.pos_y),
|
||||
width: toPixels(texture_selection_rect.width),
|
||||
height: toPixels(texture_selection_rect.height),
|
||||
}">
|
||||
</div>
|
||||
|
||||
<svg id="uv_selection_outline">
|
||||
<path :d="selection_outline" />
|
||||
<path :d="selection_outline" class="dash_overlay" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div class="uv_transparent_face" v-else-if="selected_faces.length">${tl('uv_editor.transparent_face')}</div>
|
||||
|
Loading…
Reference in New Issue
Block a user