mirror of
https://github.com/overte-org/overte.git
synced 2025-06-22 21:40:03 +02:00
* mirrors wip * fix view + projection, texture flipping, billboarding * wip portals * wip * fix cpu frustum culling (hacky?) * fix mirrors in deferred * mirrors on models + text * portals use exit as ignoreItem * cleanup * entity tags * wild guess to handle view correction, hide portalExitID in create when mirrorMode != portal * let's try this?? * plz * promising * fix paramsOffset and view flipping * portals shouldn't flip * break when tag found * fix portal view calculation * Revert "Mirrors + Portals" * Revert "Revert "Mirrors + Portals"" * web entity wantsKeyboardFocus property * fix typo * move audio zones to zone entity properties * fix audio zones in create * set dynamic factory * new procecural particle entity type * fix particle intersection * shorten create labels * fix 0 update props case * Ability to smooth model animations * sound entities * fix layered simulate items * fix stereo sound speed * support non-localOnly sound avatar entities * add sound url prompt * support registration point, improve locking * remove keyboardRasied * locking attempt #2 * fix keyboardRasied typo * add default particle props * add unlit property for shapes * Merge branch master into protocol_changes * add ambient light color * fix create issue * fix create issue * add tonemapping props to zones, wip ambient occlusion * wip ambient occlusion * it's working! * remove attachments * fix non-localOnly positional sounds not updating * change AO default to HBAO, remove from create * more graphics options * fix AO setting + effects in mirrors * fix AA in mirrors * alezia's fixes * fix haze in mirrors * add comment for SKYBOX_DISTANCE * new line * model loading priority updates over time, takes into account out of bounds, avatar entities have higher priority, and fsts can specify to wait for wearables to load before rendering * add loadPriority to model entities, working on other avatars waitForWearables * fix build error * try to fix isServer assert * fix stats + waitForWearables * Listen for click instead of release. * Reverted initial commit. Implemented hack to listen for menu click events. * Missed some reverts. * Missed another one. * Prevent duplicate actions. * Added extra needed checks. * Fix without formatting? (#91) * Hopefully fixed formatting. * Things can't be too easy. * Remove google poly * automated entity property serialization * cleanup + automate EntityPropertyFlags * text vertical alignment, use uint8_t for entity property enums, fix text recalculating too often * fix text size * Update interface/resources/controllers/keyboardMouse.json Co-authored-by: HifiExperiments <53453710+HifiExperiments@users.noreply.github.com> * fix component mode serialization * Fixed mouse look in selfie mode. * fix text debug assert on invalid or unloaded font * missed some enums * fix ADD_GROUP_PROPERTY_TO_MAP * fix PROP_GRAB_EQUIPPABLE_INDICATOR_URL missing urlPermission * fix KeyLightPropertyGroup legacy properties * fix PolyLineEntityItem::getEntityProperties * comment cmake script * fix copyright * Replaced key value with key text. Added additional comment about the specific delete key used. * weekly promoted place Highlight the first place in the list as the weekly promoted place * Fixed lingering references to `avatarIcon`. Signed-off-by: armored-dragon <publicmail@armoreddragon.com> * Adding icon for "Grab And Equip" section Adding icon for "Grab And Equip" section * Add "Grab And Equip" section Add "Grab And Equip" section for the grabbale and Equipable groups of properties. * Add files via upload * Add tooltips for the "Grab and Equip" properties Add the tooltips for the "Grab and Equip" properties * Text adjustments for grab.equippable Text adjustments for grab.equippable * Make Maturity Filter persisted Make Maturity Filter persisted and with a default value (Teen & Everyone) * Adjust the default value for maturity Adjust the default value for maturity * move "triggerable" under GRAB & EQUIP move "triggerable" under GRAB & EQUIP * Remove hifi-screenshare Cherry picked and updated from Tivoli dd5b6ea6ee5597a06603e16509640e7ed18106bb Co-authored-by: Julian Groß <julian.g@posteo.de> * Insert placeholder to not break protocol yet. * Fix incorrectly resolved merge conflict, left too much code. * Fixes based on review comments on previous PR * Remove code accidentally re-added during a conflict fix * bump protocol * rebuild fonts with full charset (NOT -allglyphs) * Attempt at fixing Windows master branch builds * Change minimum angular velocity to a lower one * Fix Uuid.NULL behavior --------- Signed-off-by: armored-dragon <publicmail@armoreddragon.com> Co-authored-by: HifiExperiments <thingsandstuffblog@gmail.com> Co-authored-by: ksuprynowicz <ksuprynowicz@post.pl> Co-authored-by: Dale Glass <51060919+daleglass@users.noreply.github.com> Co-authored-by: HifiExperiments <53453710+HifiExperiments@users.noreply.github.com> Co-authored-by: Julian Groß <julian.g@posteo.de> Co-authored-by: armored-dragon <publicmail@armoreddragon.com> Co-authored-by: Armored-Dragon <github56254@armoreddragon.com> Co-authored-by: Alezia Kurdis <60075796+AleziaKurdis@users.noreply.github.com> Co-authored-by: Maki <mxmcube@gmail.com> Co-authored-by: Dale Glass <dale@daleglass.net>
602 lines
19 KiB
QML
602 lines
19 KiB
QML
//
|
|
// NewPolyVoxDialog.qml
|
|
// Created by dr Karol Suprynowicz on May 17th, 2022
|
|
// based on NewModelDialog.qml
|
|
// qml/hifi
|
|
//
|
|
// Copyright 2017 High Fidelity, Inc.
|
|
// Copyright 2020 Vircadia contributors
|
|
// Copyright 2022-2024 Overte e.V.
|
|
//
|
|
// Distributed under the Apache License, Version 2.0.
|
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
//
|
|
|
|
import QtQuick 2.5
|
|
import QtQuick.Dialogs 1.2 as OriginalDialogs
|
|
|
|
import stylesUit 1.0
|
|
import controlsUit 1.0
|
|
import hifi.dialogs 1.0
|
|
|
|
Rectangle {
|
|
id: newPolyVoxDialog
|
|
// width: parent.width
|
|
// height: parent.height
|
|
HifiConstants { id: hifi }
|
|
color: hifi.colors.baseGray;
|
|
signal sendToScript(var message);
|
|
property bool keyboardEnabled: false
|
|
property bool keyboardRaised: false
|
|
property bool punctuationMode: false
|
|
|
|
function errorMessageBox(message) {
|
|
try {
|
|
return desktop.messageBox({
|
|
icon: hifi.icons.warning,
|
|
defaultButton: OriginalDialogs.StandardButton.Ok,
|
|
title: "Error",
|
|
text: message
|
|
});
|
|
} catch(e) {
|
|
Window.alert(message);
|
|
}
|
|
}
|
|
|
|
Item {
|
|
id: column1
|
|
anchors.rightMargin: 10
|
|
anchors.leftMargin: 10
|
|
anchors.bottomMargin: 10
|
|
anchors.topMargin: 0
|
|
anchors.top: parent.top
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
anchors.bottom: keyboard.top
|
|
|
|
ComboBox {
|
|
id: texturePreset
|
|
currentIndex: 0
|
|
|
|
property var texturePresetArray: ["Material presets",
|
|
"Grass + ground",
|
|
"Bricks",
|
|
"Stone",
|
|
"Concrete",
|
|
"Rock"]
|
|
|
|
width: 200
|
|
z: 100
|
|
transformOrigin: Item.Center
|
|
model: texturePresetArray
|
|
|
|
onCurrentIndexChanged: {
|
|
switch (currentIndex) {
|
|
// Clear texture entries
|
|
case 0:
|
|
xTextureURL.text = "";
|
|
yTextureURL.text = "";
|
|
zTextureURL.text = "";
|
|
break;
|
|
// Grass + ground
|
|
case 1:
|
|
xTextureURL.text = "qrc:///serverless/Textures/ground_5-2K/2K-ground_5-diffuse.jpg";
|
|
yTextureURL.text = "qrc:///serverless/Textures/ground_grass_gen_05.png";
|
|
zTextureURL.text = "qrc:///serverless/Textures/ground_5-2K/2K-ground_5-diffuse.jpg";
|
|
break;
|
|
// Bricks
|
|
case 2:
|
|
xTextureURL.text = "qrc:///serverless/Textures/2K-wall_stone_2-diffuse_l.jpg";
|
|
yTextureURL.text = "qrc:///serverless/Textures/2K-stone_floor_3-diffuse_l.jpg";
|
|
zTextureURL.text = "qrc:///serverless/Textures/2K-wall_stone_2-diffuse_l.jpg";
|
|
break;
|
|
// Stone
|
|
case 3:
|
|
xTextureURL.text = "qrc:///serverless/Textures/wall_l.png";
|
|
yTextureURL.text = "qrc:///serverless/Textures/floor_l.png";
|
|
zTextureURL.text = "qrc:///serverless/Textures/wall_l.png";
|
|
break;
|
|
// Concrete
|
|
case 4:
|
|
xTextureURL.text = "qrc:///serverless/Textures/concrete_12-2K/2K-concrete_12-diffuse.jpg";
|
|
yTextureURL.text = "qrc:///serverless/Textures/concrete_12-2K/2K-concrete_12-diffuse.jpg";
|
|
zTextureURL.text = "qrc:///serverless/Textures/concrete_12-2K/2K-concrete_12-diffuse.jpg";
|
|
break;
|
|
// Rock
|
|
case 5:
|
|
xTextureURL.text = "qrc:///serverless/Textures/Rock026_2K-JPG/Rock026_2K_Color.jpg";
|
|
yTextureURL.text = "qrc:///serverless/Textures/Rock026_2K-JPG/Rock026_2K_Color.jpg";
|
|
zTextureURL.text = "qrc:///serverless/Textures/Rock026_2K-JPG/Rock026_2K_Color.jpg";
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
Text {
|
|
id: text1
|
|
anchors.top: texturePreset.bottom
|
|
anchors.topMargin: 5
|
|
text: qsTr("X Texture URL <i> </i>")
|
|
color: "#ffffff"
|
|
font.pixelSize: 12
|
|
}
|
|
|
|
Button {
|
|
id: pasteBtn1
|
|
text: "Paste"
|
|
font.pixelSize: 11
|
|
height: 16
|
|
width: 40
|
|
radius: 4
|
|
anchors.top: text1.top
|
|
anchors.left: text1.right
|
|
anchors.bottom: text1.bottom
|
|
onClicked: {
|
|
xTextureURL.paste()
|
|
}
|
|
}
|
|
|
|
TextInput {
|
|
id: xTextureURL
|
|
height: 20
|
|
text: qsTr("")
|
|
color: "white"
|
|
anchors.top: pasteBtn1.bottom
|
|
anchors.topMargin: 5
|
|
anchors.left: parent.left
|
|
anchors.leftMargin: 0
|
|
anchors.right: parent.right
|
|
anchors.rightMargin: 0
|
|
font.pixelSize: 12
|
|
|
|
onAccepted: {
|
|
newPolyVoxDialog.keyboardEnabled = false;
|
|
}
|
|
|
|
onTextChanged : {
|
|
if (xTextureURL.text.length === 0){
|
|
button1.enabled = false;
|
|
} else {
|
|
button1.enabled = true;
|
|
}
|
|
}
|
|
|
|
MouseArea {
|
|
anchors.fill: parent
|
|
onClicked: {
|
|
newPolyVoxDialog.keyboardEnabled = HMD.active
|
|
parent.focus = true;
|
|
parent.forceActiveFocus();
|
|
xTextureURL.cursorPosition = xTextureURL.positionAt(mouseX, mouseY, TextInput.CursorBetweenCharaters);
|
|
}
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
id: textInputBox1
|
|
color: "white"
|
|
anchors.fill: xTextureURL
|
|
opacity: 0.1
|
|
}
|
|
|
|
Text {
|
|
id: text2
|
|
text: qsTr("Y Texture URL <i> </i>")
|
|
color: "#ffffff"
|
|
font.pixelSize: 12
|
|
anchors.top: textInputBox1.bottom
|
|
anchors.topMargin: 5
|
|
}
|
|
Button {
|
|
id: pasteBtn2
|
|
text: "Paste"
|
|
font.pixelSize: 11
|
|
height: 16
|
|
width: 40
|
|
radius: 4
|
|
anchors.top: text2.top
|
|
anchors.left: text2.right
|
|
anchors.bottom: text2.bottom
|
|
onClicked: {
|
|
yTextureURL.paste()
|
|
}
|
|
}
|
|
|
|
TextInput {
|
|
id: yTextureURL
|
|
height: 20
|
|
text: qsTr("")
|
|
color: "white"
|
|
anchors.top: pasteBtn2.bottom
|
|
anchors.topMargin: 5
|
|
anchors.left: parent.left
|
|
anchors.leftMargin: 0
|
|
anchors.right: parent.right
|
|
anchors.rightMargin: 0
|
|
font.pixelSize: 12
|
|
|
|
onAccepted: {
|
|
newPolyVoxDialog.keyboardEnabled = false;
|
|
}
|
|
|
|
onTextChanged : {
|
|
if (yTextureURL.text.length === 0){
|
|
button1.enabled = false;
|
|
} else {
|
|
button1.enabled = true;
|
|
}
|
|
}
|
|
|
|
MouseArea {
|
|
anchors.fill: parent
|
|
onClicked: {
|
|
newPolyVoxDialog.keyboardEnabled = HMD.active
|
|
parent.focus = true;
|
|
parent.forceActiveFocus();
|
|
yTextureURL.cursorPosition = yTextureURL.positionAt(mouseX, mouseY, TextInput.CursorBetweenCharaters);
|
|
}
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
id: textInputBox2
|
|
color: "white"
|
|
anchors.fill: yTextureURL
|
|
opacity: 0.1
|
|
}
|
|
|
|
Text {
|
|
id: text3
|
|
text: qsTr("Z Texture URL <i> </i>")
|
|
color: "#ffffff"
|
|
font.pixelSize: 12
|
|
anchors.top: textInputBox2.bottom
|
|
anchors.topMargin: 5
|
|
}
|
|
Button {
|
|
id: pasteBtn3
|
|
text: "Paste"
|
|
font.pixelSize: 11
|
|
height: 16
|
|
width: 40
|
|
radius: 4
|
|
anchors.top: text3.top
|
|
anchors.left: text3.right
|
|
anchors.bottom: text3.bottom
|
|
onClicked: {
|
|
zTextureURL.paste()
|
|
}
|
|
}
|
|
|
|
TextInput {
|
|
id: zTextureURL
|
|
height: 20
|
|
text: qsTr("")
|
|
color: "white"
|
|
anchors.top: pasteBtn3.bottom
|
|
anchors.topMargin: 5
|
|
anchors.left: parent.left
|
|
anchors.leftMargin: 0
|
|
anchors.right: parent.right
|
|
anchors.rightMargin: 0
|
|
font.pixelSize: 12
|
|
|
|
onAccepted: {
|
|
newPolyVoxDialog.keyboardEnabled = false;
|
|
}
|
|
|
|
onTextChanged : {
|
|
if (zTextureURL.text.length === 0){
|
|
button1.enabled = false;
|
|
} else {
|
|
button1.enabled = true;
|
|
}
|
|
}
|
|
|
|
MouseArea {
|
|
anchors.fill: parent
|
|
onClicked: {
|
|
newPolyVoxDialog.keyboardEnabled = HMD.active
|
|
parent.focus = true;
|
|
parent.forceActiveFocus();
|
|
zTextureURL.cursorPosition = zTextureURL.positionAt(mouseX, mouseY, TextInput.CursorBetweenCharaters);
|
|
}
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
id: textInputBox3
|
|
color: "white"
|
|
anchors.fill: zTextureURL
|
|
opacity: 0.1
|
|
}
|
|
|
|
Text {
|
|
id: textVolumeSize
|
|
text: qsTr("Volume Size (number of voxels along the edge)")
|
|
color: "#ffffff"
|
|
font.pixelSize: 12
|
|
anchors.top: zTextureURL.bottom
|
|
anchors.topMargin: 5
|
|
}
|
|
|
|
Row {
|
|
id: rowVolumeSize
|
|
height: 50
|
|
spacing: 30
|
|
anchors.top: textVolumeSize.bottom
|
|
anchors.topMargin: 5
|
|
anchors.left: parent.left
|
|
anchors.leftMargin: 0
|
|
anchors.right: parent.right
|
|
anchors.rightMargin: 0
|
|
|
|
Text {
|
|
id: textVolumeSizeX
|
|
text: qsTr("X")
|
|
color: "#ffffff"
|
|
font.pixelSize: 12
|
|
}
|
|
|
|
TextInput {
|
|
id: volumeSizeX
|
|
height: 20
|
|
width: 50
|
|
anchors.left: textVolumeSizeX.right
|
|
anchors.leftMargin: 3
|
|
text: qsTr("20")
|
|
color: "white"
|
|
font.pixelSize: 12
|
|
validator: IntValidator{bottom: 8; top: 64;}
|
|
|
|
onAccepted: {
|
|
newPolyVoxDialog.keyboardEnabled = false;
|
|
}
|
|
|
|
MouseArea {
|
|
anchors.fill: parent
|
|
onClicked: {
|
|
newPolyVoxDialog.keyboardEnabled = HMD.active
|
|
parent.focus = true;
|
|
parent.forceActiveFocus();
|
|
volumeSizeX.cursorPosition = volumeSizeX.positionAt(mouseX, mouseY, TextInput.CursorBetweenCharaters);
|
|
}
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
id: textInputBoxVolumeSizeX
|
|
color: "white"
|
|
anchors.fill: volumeSizeX
|
|
opacity: 0.1
|
|
}
|
|
|
|
Text {
|
|
id: textVolumeSizeY
|
|
text: qsTr("Y")
|
|
color: "#ffffff"
|
|
font.pixelSize: 12
|
|
anchors.left: volumeSizeX.right
|
|
anchors.leftMargin: 5
|
|
}
|
|
|
|
TextInput {
|
|
id: volumeSizeY
|
|
height: 20
|
|
width: 50
|
|
anchors.left: textVolumeSizeY.right
|
|
anchors.leftMargin: 3
|
|
text: qsTr("20")
|
|
color: "white"
|
|
font.pixelSize: 12
|
|
validator: IntValidator{bottom: 8; top: 64;}
|
|
|
|
onAccepted: {
|
|
newPolyVoxDialog.keyboardEnabled = false;
|
|
}
|
|
|
|
MouseArea {
|
|
anchors.fill: parent
|
|
onClicked: {
|
|
newPolyVoxDialog.keyboardEnabled = HMD.active
|
|
parent.focus = true;
|
|
parent.forceActiveFocus();
|
|
volumeSizeY.cursorPosition = volumeSizeY.positionAt(mouseX, mouseY, TextInput.CursorBetweenCharaters);
|
|
}
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
id: textInputBoxVolumeSizeY
|
|
color: "white"
|
|
anchors.fill: volumeSizeY
|
|
opacity: 0.1
|
|
}
|
|
Text {
|
|
id: textVolumeSizeZ
|
|
text: qsTr("X")
|
|
color: "#ffffff"
|
|
font.pixelSize: 12
|
|
anchors.left: volumeSizeY.right
|
|
anchors.leftMargin: 5
|
|
}
|
|
|
|
TextInput {
|
|
id: volumeSizeZ
|
|
height: 20
|
|
width: 50
|
|
anchors.left: textVolumeSizeZ.right
|
|
anchors.leftMargin: 3
|
|
text: qsTr("20")
|
|
color: "white"
|
|
font.pixelSize: 12
|
|
validator: IntValidator{bottom: 8; top: 64;}
|
|
|
|
onAccepted: {
|
|
newPolyVoxDialog.keyboardEnabled = false;
|
|
}
|
|
|
|
MouseArea {
|
|
anchors.fill: parent
|
|
onClicked: {
|
|
newPolyVoxDialog.keyboardEnabled = HMD.active
|
|
parent.focus = true;
|
|
parent.forceActiveFocus();
|
|
volumeSizeZ.cursorPosition = volumeSizeZ.positionAt(mouseX, mouseY, TextInput.CursorBetweenCharaters);
|
|
}
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
id: textInputBoxVolumeSizeZ
|
|
color: "white"
|
|
anchors.fill: volumeSizeZ
|
|
opacity: 0.1
|
|
}
|
|
}
|
|
|
|
Row {
|
|
id: row1
|
|
height: 400
|
|
spacing: 30
|
|
anchors.top: rowVolumeSize.bottom
|
|
anchors.topMargin: 5
|
|
anchors.left: parent.left
|
|
anchors.leftMargin: 0
|
|
anchors.right: parent.right
|
|
anchors.rightMargin: 0
|
|
|
|
Column {
|
|
id: column2
|
|
width: 200
|
|
height: 600
|
|
spacing: 10
|
|
|
|
|
|
CheckBox {
|
|
id: grabbable
|
|
text: qsTr("Grabbable")
|
|
}
|
|
|
|
CheckBox {
|
|
id: collisions
|
|
text: qsTr("Collisions")
|
|
}
|
|
|
|
Row {
|
|
id: row2
|
|
width: 200
|
|
height: 400
|
|
spacing: 20
|
|
|
|
}
|
|
}
|
|
|
|
Column {
|
|
id: column3
|
|
height: 400
|
|
spacing: 10
|
|
|
|
Text {
|
|
id: text4
|
|
text: qsTr("Voxel type")
|
|
color: "#ffffff"
|
|
font.pixelSize: 12
|
|
}
|
|
|
|
ComboBox {
|
|
id: surfaceStyle
|
|
currentIndex: 3
|
|
|
|
property var surfaceStyleArray: ["Marching Cubes",
|
|
"Cubic",
|
|
"Edged Cubic",
|
|
"Edged Marching Cubes"]
|
|
|
|
width: 200
|
|
z: 100
|
|
transformOrigin: Item.Center
|
|
model: surfaceStyleArray
|
|
}
|
|
|
|
Text {
|
|
id: textInitialShape
|
|
text: qsTr("Initial shape")
|
|
color: "#ffffff"
|
|
font.pixelSize: 12
|
|
}
|
|
|
|
ComboBox {
|
|
id: initialShape
|
|
currentIndex: 0
|
|
|
|
property var initialShapeArray: ["Box",
|
|
"Plane, 1/4 full",
|
|
"Plane, 3/4 full",
|
|
"Single voxel",
|
|
]
|
|
|
|
width: 200
|
|
z: 100
|
|
transformOrigin: Item.Center
|
|
model: initialShapeArray
|
|
}
|
|
|
|
Row {
|
|
id: row3
|
|
width: 200
|
|
height: 400
|
|
spacing: 5
|
|
|
|
anchors.horizontalCenter: column3.horizontalCenter
|
|
anchors.horizontalCenterOffset: -20
|
|
|
|
Button {
|
|
id: button1
|
|
text: qsTr("Create")
|
|
z: -1
|
|
enabled: false
|
|
onClicked: {
|
|
newPolyVoxDialog.sendToScript({
|
|
method: "newPolyVoxDialogAdd",
|
|
params: {
|
|
xTextureURL: xTextureURL.text,
|
|
yTextureURL: yTextureURL.text,
|
|
zTextureURL: zTextureURL.text,
|
|
volumeSizeX: volumeSizeX.text,
|
|
volumeSizeY: volumeSizeY.text,
|
|
volumeSizeZ: volumeSizeZ.text,
|
|
surfaceStyleIndex: surfaceStyle.currentIndex,
|
|
initialShapeIndex: initialShape.currentIndex,
|
|
grabbable: grabbable.checked,
|
|
collisions: collisions.checked,
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
Button {
|
|
id: button2
|
|
z: -1
|
|
text: qsTr("Cancel")
|
|
onClicked: {
|
|
newPolyVoxDialog.sendToScript({method: "newPolyVoxDialogCancel"})
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Keyboard {
|
|
id: keyboard
|
|
raised: parent.keyboardEnabled && parent.keyboardRaised
|
|
numeric: parent.punctuationMode
|
|
anchors {
|
|
bottom: parent.bottom
|
|
bottomMargin: 40
|
|
left: parent.left
|
|
right: parent.right
|
|
}
|
|
}
|
|
}
|