Merge branch 'master' into expression-colors
This commit is contained in:
@@ -2009,6 +2009,7 @@ void Application::initTypes()
|
||||
App::PropertyTime ::init();
|
||||
App::PropertyUltimateTensileStrength ::init();
|
||||
App::PropertyVacuumPermittivity ::init();
|
||||
App::PropertyVelocity ::init();
|
||||
App::PropertyVolume ::init();
|
||||
App::PropertyVolumeFlowRate ::init();
|
||||
App::PropertyVolumetricThermalExpansionCoefficient::init();
|
||||
|
||||
@@ -262,6 +262,7 @@ SET(FreeCADApp_CPP_SRCS
|
||||
ComplexGeoDataPyImp.cpp
|
||||
Enumeration.cpp
|
||||
IndexedName.cpp
|
||||
MappedName.cpp
|
||||
Material.cpp
|
||||
MaterialPyImp.cpp
|
||||
Metadata.cpp
|
||||
@@ -279,6 +280,7 @@ SET(FreeCADApp_HPP_SRCS
|
||||
ComplexGeoData.h
|
||||
Enumeration.h
|
||||
IndexedName.h
|
||||
MappedName.h
|
||||
Material.h
|
||||
Metadata.h
|
||||
)
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/****************************************************************************
|
||||
* Copyright (c) 2020 Zheng, Lei (realthunder) <[email protected]>*
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
****************************************************************************/
|
||||
|
||||
#include "PreCompiled.h"
|
||||
|
||||
#ifndef _PreComp_
|
||||
# include <unordered_set>
|
||||
#endif
|
||||
|
||||
//#include <boost/functional/hash.hpp>
|
||||
|
||||
#include "MappedName.h"
|
||||
|
||||
using namespace Data;
|
||||
|
||||
|
||||
void MappedName::compact()
|
||||
{
|
||||
|
||||
if (this->raw) {
|
||||
this->data = QByteArray(this->data.constData(), this->data.size());
|
||||
this->raw = false;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static std::unordered_set<QByteArray, ByteArrayHasher> PostfixSet;
|
||||
if (this->postfix.size()) {
|
||||
auto res = PostfixSet.insert(this->postfix);
|
||||
if (!res.second)
|
||||
self->postfix = *res.first;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -0,0 +1,908 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
/****************************************************************************
|
||||
* Copyright (c) 2022 Zheng, Lei (realthunder) <[email protected]>*
|
||||
* Copyright (c) 2023 FreeCAD Project Association *
|
||||
* *
|
||||
* This file is part of FreeCAD. *
|
||||
* *
|
||||
* FreeCAD is free software: you can redistribute it and/or modify it *
|
||||
* under the terms of the GNU Lesser General Public License as *
|
||||
* published by the Free Software Foundation, either version 2.1 of the *
|
||||
* License, or (at your option) any later version. *
|
||||
* *
|
||||
* FreeCAD is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with FreeCAD. If not, see *
|
||||
* <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef APP_MAPPED_NAME_H
|
||||
#define APP_MAPPED_NAME_H
|
||||
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QHash>
|
||||
|
||||
#include "ComplexGeoData.h"
|
||||
#include "IndexedName.h"
|
||||
|
||||
|
||||
namespace Data
|
||||
{
|
||||
|
||||
// NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic)
|
||||
|
||||
/// The MappedName class maintains a two-part name: the first part ("data") is considered immutable
|
||||
/// once created, while the second part ("postfix") can be modified/appended to by later operations.
|
||||
/// It uses shared data when possible (see the fromRawData() members). Despite storing data and
|
||||
/// postfix separately, they can be accessed via calls to size(), operator[], etc. as though they
|
||||
/// were a single array.
|
||||
class AppExport MappedName
|
||||
{
|
||||
public:
|
||||
/// Create a MappedName from a C string, optionally prefixed by an element map prefix, which
|
||||
/// will be omitted from the stored MappedName.
|
||||
///
|
||||
/// \param name The new name. A deep copy is made.
|
||||
/// \param size Optional, the length of the name string. If not provided, the string must be
|
||||
/// null-terminated.
|
||||
explicit MappedName(const char* name, int size = -1)
|
||||
: raw(false)
|
||||
{
|
||||
if (!name) {
|
||||
return;
|
||||
}
|
||||
if (boost::starts_with(name, ComplexGeoData::elementMapPrefix())) {
|
||||
name += ComplexGeoData::elementMapPrefix().size();
|
||||
}
|
||||
|
||||
data = size < 0 ? QByteArray(name) : QByteArray(name, size);
|
||||
}
|
||||
|
||||
/// Create a MappedName from a C++ std::string, optionally prefixed by an element map prefix,
|
||||
/// which will be omitted from the stored MappedName.
|
||||
///
|
||||
/// \param name The new name. A deep copy is made.
|
||||
explicit MappedName(const std::string& nameString)
|
||||
: raw(false)
|
||||
{
|
||||
auto size = nameString.size();
|
||||
const char* name = nameString.c_str();
|
||||
if (boost::starts_with(nameString, ComplexGeoData::elementMapPrefix())) {
|
||||
name += ComplexGeoData::elementMapPrefix().size();
|
||||
size -= ComplexGeoData::elementMapPrefix().size();
|
||||
}
|
||||
data = QByteArray(name, static_cast<int>(size));
|
||||
}
|
||||
|
||||
/// Create a MappedName from an IndexedName. If non-zero, the numerical part of the IndexedName
|
||||
/// is appended as text to the MappedName. In that case the memory is *not* shared between the
|
||||
/// original IndexedName and the MappedName.
|
||||
explicit MappedName(const IndexedName& element)
|
||||
: data(QByteArray::fromRawData(element.getType(), qstrlen(element.getType()))),
|
||||
raw(true)
|
||||
{
|
||||
if (element.getIndex() > 0) {
|
||||
this->data += QByteArray::number(element.getIndex());
|
||||
this->raw = false;
|
||||
}
|
||||
}
|
||||
|
||||
MappedName()
|
||||
: raw(false)
|
||||
{}
|
||||
|
||||
MappedName(const MappedName& other) = default;
|
||||
|
||||
/// Copy constructor with start position offset and optional size. The data is *not* reused.
|
||||
///
|
||||
/// \param other The MappedName to copy
|
||||
/// \param startPosition an integer offset to start the copy from
|
||||
/// \param size the number of bytes to copy.
|
||||
/// \see append() for details about how the copy behaves for various sizes and start positions
|
||||
MappedName(const MappedName& other, int startPosition, int size = -1)
|
||||
: raw(false)
|
||||
{
|
||||
append(other, startPosition, size);
|
||||
}
|
||||
|
||||
/// Copy constructor with additional postfix
|
||||
///
|
||||
/// \param other The mapped name to copy. Its data and postfix become the new MappedName's data
|
||||
/// \param postfix The postfix for the new MappedName
|
||||
MappedName(const MappedName& other, const char* postfix)
|
||||
: data(other.data + other.postfix),
|
||||
postfix(postfix),
|
||||
raw(false)
|
||||
{}
|
||||
|
||||
/// Move constructor
|
||||
MappedName(MappedName&& other) noexcept
|
||||
: data(std::move(other.data)),
|
||||
postfix(std::move(other.postfix)),
|
||||
raw(other.raw)
|
||||
{}
|
||||
|
||||
~MappedName() = default;
|
||||
|
||||
/// Construct a MappedName from raw character data (including null characters, if size is
|
||||
/// provided). No copy is made: the data is used in place.
|
||||
///
|
||||
/// \param name The raw data to use.
|
||||
/// \param size The number of bytes to access. If omitted, name must be null-terminated.
|
||||
/// \return a new MappedName with name as its data.
|
||||
static MappedName fromRawData(const char* name, int size = -1)
|
||||
{
|
||||
MappedName res;
|
||||
if (name) {
|
||||
res.data =
|
||||
QByteArray::fromRawData(name, size >= 0 ? size : static_cast<int>(qstrlen(name)));
|
||||
res.raw = true;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Construct a MappedName from QByteArray data (including any embedded null characters).
|
||||
///
|
||||
/// \param data The original data. No copy is made, the data is shared with the other instance.
|
||||
/// \return a new MappedName with data as its data.
|
||||
static MappedName fromRawData(const QByteArray& data)
|
||||
{
|
||||
return fromRawData(data.constData(), data.size());
|
||||
}
|
||||
|
||||
/// Construct a MappedName from another MappedName
|
||||
///
|
||||
/// \param other The MappedName to copy from. The data is usually not copied, but in some
|
||||
/// cases a partial copy may be made to support a slice that extends across other's data into
|
||||
/// its postfix.
|
||||
/// \param startPosition The position to start the reference at.
|
||||
/// \param size The number of bytes to access. If omitted, continues from startPosition
|
||||
/// to the end of available data (including postfix).
|
||||
/// \return a new MappedName sharing (possibly a subset of) data with other.
|
||||
/// \see append() for details about how the copy behaves for various sizes and start positions
|
||||
static MappedName fromRawData(const MappedName& other, int startPosition, int size = -1)
|
||||
{
|
||||
if (startPosition < 0) {
|
||||
startPosition = 0;
|
||||
}
|
||||
|
||||
if (startPosition >= other.size()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (startPosition >= other.data.size()) {
|
||||
return {other, startPosition, size};
|
||||
}
|
||||
|
||||
MappedName res;
|
||||
res.raw = true;
|
||||
if (size < 0) {
|
||||
size = other.size() - startPosition;
|
||||
}
|
||||
|
||||
if (size < other.data.size() - startPosition) {
|
||||
res.data = QByteArray::fromRawData(other.data.constData() + startPosition, size);
|
||||
}
|
||||
else {
|
||||
res.data = QByteArray::fromRawData(other.data.constData() + startPosition,
|
||||
other.data.size() - startPosition);
|
||||
size -= other.data.size() - startPosition;
|
||||
if (size == other.postfix.size()) {
|
||||
res.postfix = other.postfix;
|
||||
}
|
||||
else if (size != 0) {
|
||||
res.postfix.append(other.postfix.constData(), size);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Share data with another MappedName
|
||||
MappedName& operator=(const MappedName& other) = default;
|
||||
|
||||
/// Create a new MappedName from a std::string: the string's data is copied.
|
||||
MappedName& operator=(const std::string& other)
|
||||
{
|
||||
*this = MappedName(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Create a new MappedName from a const char *. The character data is copied.
|
||||
MappedName& operator=(const char* other)
|
||||
{
|
||||
*this = MappedName(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
/// Move-construct a MappedName
|
||||
MappedName& operator=(MappedName&& other) noexcept
|
||||
{
|
||||
this->data = std::move(other.data);
|
||||
this->postfix = std::move(other.postfix);
|
||||
this->raw = other.raw;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Write to a stream as the name with postfix directly appended to it. Note that there is no
|
||||
/// special handling for null or non-ASCII characters, they are simply written to the stream.
|
||||
friend std::ostream& operator<<(std::ostream& stream, const MappedName& mappedName)
|
||||
{
|
||||
stream.write(mappedName.data.constData(), mappedName.data.size());
|
||||
stream.write(mappedName.postfix.constData(), mappedName.postfix.size());
|
||||
return stream;
|
||||
}
|
||||
|
||||
/// Two MappedNames are equal if the concatenation of their data and postfix is equal. The
|
||||
/// individual data and postfix may NOT be equal in this case.
|
||||
bool operator==(const MappedName& other) const
|
||||
{
|
||||
if (this->size() != other.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this->data.size() == other.data.size()) {
|
||||
return this->data == other.data && this->postfix == other.postfix;
|
||||
}
|
||||
|
||||
const auto& smaller = this->data.size() < other.data.size() ? *this : other;
|
||||
const auto& larger = this->data.size() < other.data.size() ? other : *this;
|
||||
|
||||
if (!larger.data.startsWith(smaller.data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QByteArray tmp = QByteArray::fromRawData(larger.data.constData() + smaller.data.size(),
|
||||
larger.data.size() - smaller.data.size());
|
||||
|
||||
if (!smaller.postfix.startsWith(tmp)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
tmp = QByteArray::fromRawData(smaller.postfix.constData() + tmp.size(),
|
||||
smaller.postfix.size() - tmp.size());
|
||||
|
||||
return tmp == larger.postfix;
|
||||
}
|
||||
|
||||
bool operator!=(const MappedName& other) const
|
||||
{
|
||||
return !(this->operator==(other));
|
||||
}
|
||||
|
||||
/// Returns a new MappedName whose data is the LHS argument's data and whose postfix is the LHS
|
||||
/// argument's postfix with the RHS argument's data and postfix appended to it.
|
||||
MappedName operator+(const MappedName& other) const
|
||||
{
|
||||
MappedName res(*this);
|
||||
res += other;
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Returns a new MappedName whose data is the LHS argument's data and whose postfix is the LHS
|
||||
/// argument's postfix with the RHS argument appended to it. The character data is copied.
|
||||
MappedName operator+(const char* other) const
|
||||
{
|
||||
MappedName res(*this);
|
||||
res += other;
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Returns a new MappedName whose data is the LHS argument's data and whose postfix is the LHS
|
||||
/// argument's postfix with the RHS argument appended to it. The character data is copied.
|
||||
MappedName operator+(const std::string& other) const
|
||||
{
|
||||
MappedName res(*this);
|
||||
res += other;
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Returns a new MappedName whose data is the LHS argument's data and whose postfix is the LHS
|
||||
/// argument's postfix with the RHS argument appended to it.
|
||||
MappedName operator+(const QByteArray& other) const
|
||||
{
|
||||
MappedName res(*this);
|
||||
res += other;
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Appends other to this instance's postfix. other must be a null-terminated C string. The
|
||||
/// character data from the string is copied.
|
||||
MappedName& operator+=(const char* other)
|
||||
{
|
||||
if (other && (other[0] != 0)) {
|
||||
this->postfix.append(other, -1);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Appends other to this instance's postfix. The character data from the string is copied.
|
||||
MappedName& operator+=(const std::string& other)
|
||||
{
|
||||
if (!other.empty()) {
|
||||
this->postfix.reserve(this->postfix.size() + static_cast<int>(other.size()));
|
||||
this->postfix.append(other.c_str(), static_cast<int>(other.size()));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Appends other to this instance's postfix. The data may be either copied or shared, depending
|
||||
/// on whether this->postfix is empty (in which case the data is shared) or non-empty (in which
|
||||
/// case it is copied).
|
||||
MappedName& operator+=(const QByteArray& other)
|
||||
{
|
||||
this->postfix += other;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Appends other to this instance's postfix, unless this is empty, in which case this acts
|
||||
/// like operator=, and makes this instance's data equal to other's data, and this instance's
|
||||
/// postfix equal to the other instance's postfix.
|
||||
MappedName& operator+=(const MappedName& other)
|
||||
{
|
||||
append(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Add dataToAppend to this MappedName. If the current name is empty, this becomes the new
|
||||
/// data element. If this MappedName already has data, then the data is appended to the postfix.
|
||||
///
|
||||
/// \param dataToAppend The data to add. A deep copy is made.
|
||||
/// \param size The number of bytes to copy. If omitted, dataToAppend must be null-terminated.
|
||||
void append(const char* dataToAppend, int size = -1)
|
||||
{
|
||||
if (dataToAppend && (size != 0)) {
|
||||
if (size < 0) {
|
||||
size = static_cast<int>(qstrlen(dataToAppend));
|
||||
}
|
||||
if (empty()) {
|
||||
this->data.append(dataToAppend, size);
|
||||
}
|
||||
else {
|
||||
this->postfix.append(dataToAppend, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Treating both this and other as single continuous byte arrays, append other to this. If this
|
||||
/// is empty, then other's data is shared with this instance's data beginning at startPosition.
|
||||
/// If this is *not* empty, then all data is appended to the postfix. If the copy crosses the
|
||||
/// boundary between other's data and its postfix, then if this instance was empty, the new
|
||||
/// data stops where other's data stops, and the remainder of the copy is placed in the suffix.
|
||||
/// Otherwise the copy simply continues as though there was no distinction between other's
|
||||
/// data and suffix.
|
||||
///
|
||||
/// \param other The MappedName to obtain the data from. The data is shared when possible,
|
||||
/// depending on the details of startPosition, size, and this->empty().
|
||||
/// \param startPosition The byte to start the copy at. Must be a positive non-zero integer less
|
||||
/// than the length of other's combined data + postfix.
|
||||
/// \param size The number of bytes to copy. Must not overrun the end of other's combined data
|
||||
/// storage when taking startPosition into consideration.
|
||||
void append(const MappedName& other, int startPosition = 0, int size = -1)
|
||||
{
|
||||
// enforce 0 <= startPosition <= other.size
|
||||
if (startPosition < 0) {
|
||||
startPosition = 0;
|
||||
}
|
||||
else if (startPosition > other.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// enforce 0 <= size <= other.size - startPosition
|
||||
if (size < 0 || size > other.size() - startPosition) {
|
||||
size = other.size() - startPosition;
|
||||
}
|
||||
|
||||
|
||||
if (startPosition < other.data.size())// if starting inside data
|
||||
{
|
||||
int count = size;
|
||||
// make sure count doesn't exceed data size and end up in postfix
|
||||
if (count > other.data.size() - startPosition) {
|
||||
count = other.data.size() - startPosition;
|
||||
}
|
||||
|
||||
// if this is empty append in data else append in postfix
|
||||
if (startPosition == 0 && count == other.data.size() && this->empty()) {
|
||||
this->data = other.data;
|
||||
this->raw = other.raw;
|
||||
}
|
||||
else {
|
||||
append(other.data.constData() + startPosition, count);
|
||||
}
|
||||
|
||||
// setup startPosition and count to continue appending the remainder to postfix
|
||||
startPosition = 0;
|
||||
size -= count;
|
||||
}
|
||||
else// else starting inside postfix
|
||||
{
|
||||
startPosition -= other.data.size();
|
||||
}
|
||||
|
||||
// if there is still data to be added to postfix
|
||||
if (size != 0) {
|
||||
if (startPosition == 0 && size == other.postfix.size()) {
|
||||
if (this->empty()) {
|
||||
this->data = other.postfix;
|
||||
}
|
||||
else if (this->postfix.isEmpty()) {
|
||||
this->postfix = other.postfix;
|
||||
}
|
||||
else {
|
||||
this->postfix += other.postfix;
|
||||
}
|
||||
}
|
||||
else {
|
||||
append(other.postfix.constData() + startPosition, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a std::string from this instance, starting at startPosition, and extending len bytes.
|
||||
///
|
||||
/// \param startPosition The offset into the data
|
||||
/// \param len The number of bytes to output
|
||||
/// \return A new std::string containing the bytes copied from this instance's data and postfix
|
||||
/// (depending on startPosition and len).
|
||||
/// \note No effort is made to ensure that these are valid ASCII characters, and it is possible
|
||||
/// the data includes embedded null characters, non-ASCII data, etc.
|
||||
std::string toString(int startPosition = 0, int len = -1) const
|
||||
{
|
||||
std::string res;
|
||||
return appendToBuffer(res, startPosition, len);
|
||||
}
|
||||
|
||||
/// Given a (possibly non-empty) std::string buffer, append this instance to it, starting at a
|
||||
/// specified position, and continuing for a specified number of bytes.
|
||||
///
|
||||
/// \param buffer The string buffer to append to.
|
||||
/// \param startPosition The position in this instance's data/postfix to start at (defaults to
|
||||
/// zero). Must be less than the total length of the data plus the postfix.
|
||||
/// \param len The number of bytes to append. If omitted, defaults to appending all available
|
||||
/// data starting at startPosition.
|
||||
/// \return A pointer to the beginning of the appended data within buffer.
|
||||
/// \note No effort is made to ensure that these are valid ASCII characters, and it is possible
|
||||
/// the data includes embedded null characters, non-ASCII data, etc.
|
||||
const char* appendToBuffer(std::string& buffer, int startPosition = 0, int len = -1) const
|
||||
{
|
||||
std::size_t offset = buffer.size();
|
||||
int count = this->size();
|
||||
if (startPosition < 0) {
|
||||
startPosition = 0;
|
||||
}
|
||||
else if (startPosition >= count) {
|
||||
return buffer.c_str() + buffer.size();
|
||||
}
|
||||
if (len < 0 || len > count - startPosition) {
|
||||
len = count - startPosition;
|
||||
}
|
||||
buffer.reserve(buffer.size() + len);
|
||||
if (startPosition < this->data.size()) {
|
||||
count = this->data.size() - startPosition;
|
||||
if (len < count) {
|
||||
count = len;
|
||||
}
|
||||
buffer.append(this->data.constData() + startPosition, count);
|
||||
len -= count;
|
||||
}
|
||||
buffer.append(this->postfix.constData(), len);
|
||||
return buffer.c_str() + offset;
|
||||
}
|
||||
|
||||
// if offset is inside data return data, if offset is > data.size
|
||||
//(ends up in postfix) return postfix
|
||||
const char* toConstString(int offset, int& size) const
|
||||
{
|
||||
if (offset < 0) {
|
||||
offset = 0;
|
||||
}
|
||||
if (offset > this->data.size()) {
|
||||
offset -= this->data.size();
|
||||
if (offset > this->postfix.size()) {
|
||||
size = 0;
|
||||
return "";
|
||||
}
|
||||
size = this->postfix.size() - offset;
|
||||
return this->postfix.constData() + offset;
|
||||
}
|
||||
size = this->data.size() - offset;
|
||||
return this->data.constData() + offset;
|
||||
}
|
||||
|
||||
/// Get access to raw byte data. When possible, data is shared between this instance and the
|
||||
/// returned QByteArray. If the combination of offset and size results in data that crosses the
|
||||
/// boundary between this->data and this->postfix, the data must be copied in order to provide
|
||||
/// access as a continuous array of bytes.
|
||||
///
|
||||
/// \param offset The start position of the raw data access.
|
||||
/// \param size The number of bytes to access. If omitted, the resulting QByteArray includes
|
||||
/// everything starting from offset to the end, including any postfix data.
|
||||
/// \return A new QByteArray that shares data with this instance if possible, or is a new copy
|
||||
/// if required by offset and size.
|
||||
QByteArray toRawBytes(int offset = 0, int size = -1) const
|
||||
{
|
||||
if (offset < 0) {
|
||||
offset = 0;
|
||||
}
|
||||
if (offset >= this->size()) {
|
||||
return {};
|
||||
}
|
||||
if (size < 0 || size > this->size() - offset) {
|
||||
size = this->size() - offset;
|
||||
}
|
||||
if (offset >= this->data.size()) {
|
||||
offset -= this->data.size();
|
||||
return QByteArray::fromRawData(this->postfix.constData() + offset, size);
|
||||
}
|
||||
if (size <= this->data.size() - offset) {
|
||||
return QByteArray::fromRawData(this->data.constData() + offset, size);
|
||||
}
|
||||
|
||||
QByteArray res(this->data.constData() + offset, this->data.size() - offset);
|
||||
res.append(this->postfix.constData(), size - this->data.size() + offset);
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Direct access to the stored QByteArray of data. A copy is never made.
|
||||
const QByteArray& dataBytes() const
|
||||
{
|
||||
return this->data;
|
||||
}
|
||||
|
||||
/// Direct access to the stored QByteArray of postfix. A copy is never made.
|
||||
const QByteArray& postfixBytes() const
|
||||
{
|
||||
return this->postfix;
|
||||
}
|
||||
|
||||
/// Convenience function providing access to the pointer to the beginning of the postfix data.
|
||||
const char* constPostfix() const
|
||||
{
|
||||
return this->postfix.constData();
|
||||
}
|
||||
|
||||
// No constData() because 'data' is allowed to contain raw data, which may not end with 0.
|
||||
|
||||
/// Provide access to the content of this instance. If either postfix or data is empty, no copy
|
||||
/// is made and the original QByteArray is returned, sharing data with this instance. If this
|
||||
/// instance contains both data and postfix, a new QByteArray is created and stores a copy of
|
||||
/// the data and postfix concatenated together.
|
||||
QByteArray toBytes() const
|
||||
{
|
||||
if (this->postfix.isEmpty()) {
|
||||
return this->data;
|
||||
}
|
||||
if (this->data.isEmpty()) {
|
||||
return this->postfix;
|
||||
}
|
||||
return this->data + this->postfix;
|
||||
}
|
||||
|
||||
/// Create an IndexedName from the data portion of this MappedName. If this data has a postfix,
|
||||
/// the function returns an empty IndexedName. The function will fail if this->data contains
|
||||
/// anything other than the ASCII letter a-z, A-Z, and the underscore, with an optional integer
|
||||
/// suffix, returning an empty IndexedName (e.g. an IndexedName that evaluates to boolean
|
||||
/// false and isNull() == true).
|
||||
///
|
||||
/// \return a new IndexedName that shares its data with this instance's data member.
|
||||
IndexedName toIndexedName() const
|
||||
{
|
||||
if (this->postfix.isEmpty()) {
|
||||
return IndexedName(this->data);
|
||||
}
|
||||
return IndexedName();
|
||||
}
|
||||
|
||||
/// Create and return a string version of this MappedName prefixed by the ComplexGeoData element
|
||||
/// map prefix, if this MappedName cannot be converted to an indexed name.
|
||||
std::string toPrefixedString() const
|
||||
{
|
||||
std::string res;
|
||||
appendToBufferWithPrefix(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Append this MappedName to a provided string buffer, including the ComplexGeoData element
|
||||
/// map prefix if the MappedName cannot be converted to an IndexedName.
|
||||
///
|
||||
/// \param buf A (possibly non-empty) string to append this MappedName to.
|
||||
/// \return A pointer to the beginning of the buffer.
|
||||
const char* appendToBufferWithPrefix(std::string& buf) const
|
||||
{
|
||||
if (!toIndexedName()) {
|
||||
buf += ComplexGeoData::elementMapPrefix();
|
||||
}
|
||||
appendToBuffer(buf);
|
||||
return buf.c_str();
|
||||
}
|
||||
|
||||
/// Equivalent to C++20 operator<=>. Performs byte-by-byte comparison of this and other,
|
||||
/// starting at the first byte and continuing through both data and postfix, ignoring which is
|
||||
/// which. If the combined data and postfix members are of unequal size but start with the same
|
||||
/// data, the shorter array is considered "less than" the longer.
|
||||
int compare(const MappedName& other) const
|
||||
{
|
||||
int thisSize = this->size();
|
||||
int otherSize = other.size();
|
||||
for (int i = 0, count = std::min(thisSize, otherSize); i < count; ++i) {
|
||||
char thisChar = this->operator[](i);
|
||||
char otherChar = other[i];
|
||||
if (thisChar < otherChar) {
|
||||
return -1;
|
||||
}
|
||||
if (thisChar > otherChar) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (thisSize < otherSize) {
|
||||
return -1;
|
||||
}
|
||||
if (thisSize > otherSize) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// \see compare()
|
||||
bool operator<(const MappedName& other) const
|
||||
{
|
||||
return compare(other) < 0;
|
||||
}
|
||||
|
||||
/// Treat this MappedName as a single continuous array of bytes, beginning with data and
|
||||
/// continuing through postfix. No bounds checking is performed when compiled in release mode.
|
||||
char operator[](int index) const
|
||||
{
|
||||
if (index < 0) {
|
||||
index = 0;
|
||||
}
|
||||
if (index >= this->data.size()) {
|
||||
if (index - this->data.size() > this->postfix.size() - 1) {
|
||||
index = this->postfix.size() - 1;
|
||||
}
|
||||
return this->postfix[index - this->data.size()];
|
||||
}
|
||||
return this->data[index];
|
||||
}
|
||||
|
||||
/// Treat this MappedName as a single continuous array of bytes, returning the combined size
|
||||
/// of the data and postfix.
|
||||
int size() const
|
||||
{
|
||||
return this->data.size() + this->postfix.size();
|
||||
}
|
||||
|
||||
/// Treat this MappedName as a single continuous array of bytes, returning true only if both
|
||||
/// data and prefix are empty.
|
||||
bool empty() const
|
||||
{
|
||||
return this->data.isEmpty() && this->postfix.isEmpty();
|
||||
}
|
||||
|
||||
/// Returns true if this is shared data, or false if a unique copy has been made.
|
||||
/// It is safe to access data only if it has been copied prior. To force a copy
|
||||
/// please \see compact()
|
||||
bool isRaw() const
|
||||
{
|
||||
return this->raw;
|
||||
}
|
||||
|
||||
/// If this is shared data, a new unshared copy is made and returned. If it is already unshared
|
||||
/// no new copy is made, a new instance is returned that shares is data with the current
|
||||
/// instance.
|
||||
MappedName copy() const
|
||||
{
|
||||
if (!this->raw) {
|
||||
return *this;
|
||||
}
|
||||
MappedName res;
|
||||
res.data.append(this->data.constData(), this->data.size());
|
||||
res.postfix = this->postfix;
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Ensure that this data is unshared, making a copy if necessary.
|
||||
void compact();
|
||||
|
||||
/// Boolean conversion is the inverse of empty(), returning true if there is data in either the
|
||||
/// data or postfix, and false if there is nothing in either.
|
||||
explicit operator bool() const
|
||||
{
|
||||
return !empty();
|
||||
}
|
||||
|
||||
/// Reset this instance, clearing anything in data and postfix.
|
||||
void clear()
|
||||
{
|
||||
this->data.clear();
|
||||
this->postfix.clear();
|
||||
this->raw = false;
|
||||
}
|
||||
|
||||
/// Find a string of characters in this MappedName. The bytes must occur either entirely in the
|
||||
/// data, or entirely in the postfix: a string that overlaps the two will not be found.
|
||||
///
|
||||
/// \param searchTarget A null-terminated C string to search for.
|
||||
/// \param startPosition A byte offset to start the search at.
|
||||
/// \return The position of the target in this instance, or -1 if the target is not found.
|
||||
int find(const char* searchTarget, int startPosition = 0) const
|
||||
{
|
||||
if (!searchTarget) {
|
||||
return -1;
|
||||
}
|
||||
if (startPosition < 0) {
|
||||
startPosition = 0;
|
||||
}
|
||||
if (startPosition < this->data.size()) {
|
||||
int res = this->data.indexOf(searchTarget, startPosition);
|
||||
if (res >= 0) {
|
||||
return res;
|
||||
}
|
||||
startPosition = 0;
|
||||
}
|
||||
else {
|
||||
startPosition -= this->data.size();
|
||||
}
|
||||
int res = this->postfix.indexOf(searchTarget, startPosition);
|
||||
if (res < 0) {
|
||||
return res;
|
||||
}
|
||||
return res + this->data.size();
|
||||
}
|
||||
|
||||
/// Find a string of characters in this MappedName. The bytes must occur either entirely in the
|
||||
/// data, or entirely in the postfix: a string that overlaps the two will not be found.
|
||||
///
|
||||
/// \param searchTarget A string to search for.
|
||||
/// \param startPosition A byte offset to start the search at.
|
||||
/// \return The position of the target in this instance, or -1 if the target is not found.
|
||||
int find(const std::string& searchTarget, int startPosition = 0) const
|
||||
{
|
||||
return find(searchTarget.c_str(), startPosition);
|
||||
}
|
||||
|
||||
/// Find a string of characters in this MappedName, starting at the back of postfix and
|
||||
/// proceeding in reverse through the data. The bytes must occur either entirely in the
|
||||
/// data, or entirely in the postfix: a string that overlaps the two will not be found.
|
||||
///
|
||||
/// \param searchTarget A null-terminated C string to search for.
|
||||
/// \param startPosition A byte offset to start the search at. Negative numbers are supported
|
||||
/// and count back from the end of the concatenated data (as in QByteArray::lastIndexOf()).
|
||||
/// \return The position of the target in this instance, or -1 if the target is not found.
|
||||
int rfind(const char* searchTarget, int startPosition = -1) const
|
||||
{
|
||||
if (!searchTarget) {
|
||||
return -1;
|
||||
}
|
||||
if (startPosition < 0
|
||||
|| startPosition >= this->data.size()) {
|
||||
if (startPosition >= data.size()) {
|
||||
startPosition -= data.size();
|
||||
}
|
||||
int res = this->postfix.lastIndexOf(searchTarget, startPosition);
|
||||
if (res >= 0) {
|
||||
return res + this->data.size();
|
||||
}
|
||||
startPosition = -1;
|
||||
}
|
||||
return this->data.lastIndexOf(searchTarget, startPosition);
|
||||
}
|
||||
|
||||
/// Find a string in this MappedName, starting at the back of postfix and proceeding in reverse
|
||||
/// through the data. The bytes must occur either entirely in the data, or entirely in the
|
||||
/// postfix: a string that overlaps the two will not be found.
|
||||
///
|
||||
/// \param searchTarget A null-terminated C string to search for.
|
||||
/// \param startPosition A byte offset to start the search at. Negative numbers are supported
|
||||
/// and count back from the end of the concatenated data (as in QByteArray::lastIndexOf()).
|
||||
/// \return The position of the target in this instance, or -1 if the target is not found.
|
||||
int rfind(const std::string& searchTarget, int startPosition = -1) const
|
||||
{
|
||||
return rfind(searchTarget.c_str(), startPosition);
|
||||
}
|
||||
|
||||
/// Returns true if this MappedName ends with the search target. If there is a postfix, only the
|
||||
/// postfix is considered. If not, then only the data is considered. A search string that
|
||||
/// overlaps the two will not be found.
|
||||
bool endsWith(const char* searchTarget) const
|
||||
{
|
||||
if (!searchTarget) {
|
||||
return false;
|
||||
}
|
||||
if (this->postfix.size() != 0) {
|
||||
return this->postfix.endsWith(searchTarget);
|
||||
}
|
||||
return this->data.endsWith(searchTarget);
|
||||
}
|
||||
|
||||
/// Returns true if this MappedName ends with the search target. If there is a postfix, only the
|
||||
/// postfix is considered. If not, then only the data is considered. A search string that
|
||||
/// overlaps the two will not be found.
|
||||
bool endsWith(const std::string& searchTarget) const
|
||||
{
|
||||
return endsWith(searchTarget.c_str());
|
||||
}
|
||||
|
||||
/// Returns true if this MappedName starts with the search target. If there is a postfix, only
|
||||
/// the postfix is considered. If not, then only the data is considered. A search string that
|
||||
/// overlaps the two will not be found.
|
||||
///
|
||||
/// \param searchTarget An array of bytes to match
|
||||
/// \param offset An offset to perform the match at
|
||||
/// \return True if this MappedName begins with the target bytes
|
||||
bool startsWith(const QByteArray& searchTarget, int offset = 0) const
|
||||
{
|
||||
if (searchTarget.size() > size() - offset) {
|
||||
return false;
|
||||
}
|
||||
if ((offset != 0)
|
||||
|| ((this->data.size() != 0) && this->data.size() < searchTarget.size())) {
|
||||
return toRawBytes(offset, searchTarget.size()) == searchTarget;
|
||||
}
|
||||
if (this->data.size() != 0) {
|
||||
return this->data.startsWith(searchTarget);
|
||||
}
|
||||
return this->postfix.startsWith(searchTarget);
|
||||
}
|
||||
|
||||
/// Returns true if this MappedName starts with the search target. If there is a postfix, only
|
||||
/// the postfix is considered. If not, then only the data is considered. A search string that
|
||||
/// overlaps the two will not be found.
|
||||
///
|
||||
/// \param searchTarget An array of bytes to match
|
||||
/// \param offset An offset to perform the match at
|
||||
/// \return True if this MappedName begins with the target bytes
|
||||
bool startsWith(const char* searchTarget, int offset = 0) const
|
||||
{
|
||||
if (!searchTarget) {
|
||||
return false;
|
||||
}
|
||||
return startsWith(
|
||||
QByteArray::fromRawData(searchTarget, static_cast<int>(qstrlen(searchTarget))), offset);
|
||||
}
|
||||
|
||||
/// Returns true if this MappedName starts with the search target. If there is a postfix, only
|
||||
/// the postfix is considered. If not, then only the data is considered. A search string that
|
||||
/// overlaps the two will not be found.
|
||||
///
|
||||
/// \param searchTarget A string to match
|
||||
/// \param offset An offset to perform the match at
|
||||
/// \return True if this MappedName begins with the target bytes
|
||||
bool startsWith(const std::string& searchTarget, int offset = 0) const
|
||||
{
|
||||
return startsWith(
|
||||
QByteArray::fromRawData(searchTarget.c_str(), static_cast<int>(searchTarget.size())),
|
||||
offset);
|
||||
}
|
||||
|
||||
/// Get a hash for this MappedName
|
||||
std::size_t hash() const
|
||||
{
|
||||
return qHash(data, qHash(postfix));
|
||||
}
|
||||
|
||||
private:
|
||||
QByteArray data;
|
||||
QByteArray postfix;
|
||||
bool raw;
|
||||
};
|
||||
|
||||
// NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic)
|
||||
|
||||
|
||||
}// namespace Data
|
||||
|
||||
|
||||
#endif// APP_MAPPED_NAME_H
|
||||
@@ -103,6 +103,9 @@
|
||||
</item>
|
||||
<item row="1" column="2" colspan="2">
|
||||
<widget class="Gui::AccelLineEdit" name="accelLineEditShortcut">
|
||||
<property name="toolTip">
|
||||
<string>To change a current shortcut enter the new shortcut in the field below and press 'Assign'.</string>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
|
||||
@@ -234,7 +234,7 @@ void NotificationAreaObserver::SendLog(const std::string& notifiername, const st
|
||||
.trimmed();// remove any leading and trailing whitespace character ('\n')
|
||||
|
||||
// avoid processing empty strings
|
||||
if(simplifiedstring.isEmpty())
|
||||
if (simplifiedstring.isEmpty())
|
||||
return;
|
||||
|
||||
if (level == Base::LogStyle::TranslatedNotification) {
|
||||
@@ -827,7 +827,7 @@ void NotificationArea::pushNotification(const QString& notifiername, const QStri
|
||||
auto timer_thread = pImp->inhibitTimer.thread();
|
||||
auto current_thread = QThread::currentThread();
|
||||
|
||||
if(timer_thread == current_thread)
|
||||
if (timer_thread == current_thread)
|
||||
pImp->inhibitTimer.start(pImp->inhibitNotificationTime);
|
||||
}
|
||||
|
||||
@@ -939,7 +939,8 @@ void NotificationArea::showInNotificationArea()
|
||||
iconstr = QStringLiteral(":/icons/info.svg");
|
||||
}
|
||||
|
||||
QString tmpmessage = convertFromPlainText(item->msg, Qt::WhiteSpaceMode::WhiteSpaceNormal);
|
||||
QString tmpmessage =
|
||||
convertFromPlainText(item->msg, Qt::WhiteSpaceMode::WhiteSpaceNormal);
|
||||
|
||||
msgw +=
|
||||
QString::fromLatin1(
|
||||
@@ -990,11 +991,17 @@ void NotificationArea::showInNotificationArea()
|
||||
|
||||
msgw += QString::fromLatin1("</table></p>");
|
||||
|
||||
// Calculate the main window QRect in global screen coordinates.
|
||||
auto mainwindow = getMainWindow();
|
||||
auto mainwindowrect = mainwindow->rect();
|
||||
auto globalmainwindowrect =
|
||||
QRect(mainwindow->mapToGlobal(mainwindowrect.topLeft()), mainwindowrect.size());
|
||||
|
||||
NotificationBox::showText(this->mapToGlobal(QPoint()),
|
||||
msgw,
|
||||
pImp->notificationExpirationTime,
|
||||
pImp->minimumOnScreenTime,
|
||||
globalmainwindowrect,
|
||||
pImp->notificationWidth);
|
||||
}
|
||||
}
|
||||
|
||||
+34
-14
@@ -73,6 +73,8 @@ public:
|
||||
bool notificationLabelChanged(const QString& text);
|
||||
/// Place the notification at the given position
|
||||
void placeNotificationLabel(const QPoint& pos);
|
||||
/// Set the windowrect defining an area to which the label should be constrained
|
||||
void setTipRect(const QRect &restrictionarea);
|
||||
|
||||
/// The instance
|
||||
static qobject_delete_later_unique_ptr<NotificationLabel> instance;
|
||||
@@ -91,6 +93,8 @@ private:
|
||||
int minShowTime;
|
||||
QTimer hideTimer;
|
||||
QTimer expireTimer;
|
||||
|
||||
QRect restrictionArea;
|
||||
};
|
||||
|
||||
qobject_delete_later_unique_ptr<NotificationLabel> NotificationLabel::instance = nullptr;
|
||||
@@ -263,25 +267,36 @@ void NotificationLabel::placeNotificationLabel(const QPoint& pos)
|
||||
|
||||
p += offset;
|
||||
|
||||
QRect screenRect = screen->geometry();
|
||||
QRect actinglimit = screen->geometry();
|
||||
|
||||
if (p.x() + this->width() > screenRect.x() + screenRect.width())
|
||||
p.rx() -= 4 + this->width();
|
||||
if (p.y() + this->height() > screenRect.y() + screenRect.height())
|
||||
p.ry() -= 24 + this->height();
|
||||
if (p.y() < screenRect.y())
|
||||
p.setY(screenRect.y());
|
||||
if (p.x() + this->width() > screenRect.x() + screenRect.width())
|
||||
p.setX(screenRect.x() + screenRect.width() - this->width());
|
||||
if (p.x() < screenRect.x())
|
||||
p.setX(screenRect.x());
|
||||
if (p.y() + this->height() > screenRect.y() + screenRect.height())
|
||||
p.setY(screenRect.y() + screenRect.height() - this->height());
|
||||
if(!restrictionArea.isNull())
|
||||
actinglimit = restrictionArea;
|
||||
|
||||
const int standard_x_padding = 4;
|
||||
const int standard_y_padding = 24;
|
||||
|
||||
if (p.x() + this->width() > actinglimit.x() + actinglimit.width())
|
||||
p.rx() -= standard_x_padding + this->width();
|
||||
if (p.y() + standard_y_padding + this->height() > actinglimit.y() + actinglimit.height())
|
||||
p.ry() -= standard_y_padding + this->height();
|
||||
if (p.y() < actinglimit.y())
|
||||
p.setY(actinglimit.y());
|
||||
if (p.x() + this->width() > actinglimit.x() + actinglimit.width())
|
||||
p.setX(actinglimit.x() + actinglimit.width() - this->width());
|
||||
if (p.x() < actinglimit.x())
|
||||
p.setX(actinglimit.x());
|
||||
if (p.y() + this->height() > actinglimit.y() + actinglimit.height())
|
||||
p.setY(actinglimit.y() + actinglimit.height() - this->height());
|
||||
}
|
||||
|
||||
this->move(p);
|
||||
}
|
||||
|
||||
void NotificationLabel::setTipRect(const QRect &restrictionarea)
|
||||
{
|
||||
restrictionArea = restrictionarea;
|
||||
}
|
||||
|
||||
bool NotificationLabel::notificationLabelChanged(const QString& text)
|
||||
{
|
||||
return NotificationLabel::instance->text() != text;
|
||||
@@ -290,7 +305,7 @@ bool NotificationLabel::notificationLabelChanged(const QString& text)
|
||||
/***************************** NotificationBox **********************************/
|
||||
|
||||
void NotificationBox::showText(const QPoint& pos, const QString& text, int displayTime,
|
||||
unsigned int minShowTime, int width)
|
||||
unsigned int minShowTime, const QRect &restrictionarea, int width)
|
||||
{
|
||||
// a label does already exist
|
||||
if (NotificationLabel::instance && NotificationLabel::instance->isVisible()) {
|
||||
@@ -301,6 +316,7 @@ void NotificationBox::showText(const QPoint& pos, const QString& text, int displ
|
||||
else {
|
||||
// If the label has changed, reuse the one that is showing (removes flickering)
|
||||
if (NotificationLabel::instance->notificationLabelChanged(text)) {
|
||||
NotificationLabel::instance->setTipRect(restrictionarea);
|
||||
NotificationLabel::instance->reuseNotification(text, displayTime, pos, width);
|
||||
NotificationLabel::instance->placeNotificationLabel(pos);
|
||||
}
|
||||
@@ -310,12 +326,16 @@ void NotificationBox::showText(const QPoint& pos, const QString& text, int displ
|
||||
|
||||
// no label can be reused, create new label:
|
||||
if (!text.isEmpty()) {
|
||||
// Note: The Label takes no parent, as on windows, we can't use the widget as parent
|
||||
// otherwise the window will be raised when the tooltip will be shown. We do not use
|
||||
// it on Linux either for consistency.
|
||||
new NotificationLabel(text,
|
||||
pos,
|
||||
displayTime,
|
||||
minShowTime,
|
||||
width);// sets NotificationLabel::instance to itself
|
||||
|
||||
NotificationLabel::instance->setTipRect(restrictionarea);
|
||||
NotificationLabel::instance->placeNotificationLabel(pos);
|
||||
NotificationLabel::instance->setObjectName(QLatin1String("NotificationBox_label"));
|
||||
|
||||
|
||||
@@ -54,11 +54,16 @@ public:
|
||||
* an event, see class documentation above)
|
||||
* @param minShowTime Time during which the notification can only be made disappear by popping
|
||||
* it out (clicking inside it).
|
||||
* @param width Fixes the width of the notification. Default value makes the width to be system determined (dependent on
|
||||
* the text).
|
||||
* @param restrictionarea Try to keep the NotificationBox within this area. If this area is not
|
||||
* provided, the whole screen is used as restriction area. This are must be provided in global
|
||||
* screen coordinates.
|
||||
* @param width Fixes the width of the notification. Default value makes the width to be system
|
||||
* determined (dependent on the text). If a fixed width is provided it is enforced over the
|
||||
* restrictionarea.
|
||||
*/
|
||||
static void showText(const QPoint& pos, const QString& text, int displayTime = -1,
|
||||
unsigned int minShowTime = 0, int width = 0);
|
||||
unsigned int minShowTime = 0, const QRect& restrictionarea = {},
|
||||
int width = 0);
|
||||
/// Hides a notification.
|
||||
static inline void hideText()
|
||||
{
|
||||
|
||||
@@ -153,7 +153,7 @@ inline void Gui::Notify(TNotifier && notifier, TCaption && caption, TMessage &&
|
||||
|
||||
if constexpr( type == Base::LogStyle::TranslatedNotification) {
|
||||
// trailing newline is necessary as this may be shown too in a console requiring them (depending on the configuration).
|
||||
auto msg = message.append(QStringLiteral("\n")); // QString
|
||||
auto msg = QStringLiteral("%1. %2\n").arg(caption).arg(message); // QString
|
||||
|
||||
if constexpr( std::is_base_of_v<App::DocumentObject, std::remove_pointer_t<typename std::decay<TNotifier>::type>> ) {
|
||||
Base::Console().Send<type>(notifier->getFullLabel(), msg.toUtf8());
|
||||
|
||||
+25
-33
@@ -35,13 +35,9 @@
|
||||
using namespace Gui::Dialog;
|
||||
|
||||
/** Construction */
|
||||
PropertyPage::PropertyPage(QWidget* parent) : QWidget(parent)
|
||||
{
|
||||
bChanged = false;
|
||||
}
|
||||
|
||||
/** Destruction */
|
||||
PropertyPage::~PropertyPage()
|
||||
PropertyPage::PropertyPage(QWidget* parent)
|
||||
: QWidget(parent)
|
||||
, bChanged{false}
|
||||
{
|
||||
}
|
||||
|
||||
@@ -61,40 +57,40 @@ void PropertyPage::reset()
|
||||
}
|
||||
|
||||
/** Returns whether the page was modified or not. */
|
||||
bool PropertyPage::isModified()
|
||||
bool PropertyPage::isModified() const
|
||||
{
|
||||
return bChanged;
|
||||
return bChanged;
|
||||
}
|
||||
|
||||
/** Sets the page to be modified. */
|
||||
void PropertyPage::setModified(bool b)
|
||||
void PropertyPage::setModified(bool value)
|
||||
{
|
||||
bChanged = b;
|
||||
bChanged = value;
|
||||
}
|
||||
|
||||
/** Applies all changes calling @ref apply() and resets the modified state. */
|
||||
void PropertyPage::onApply()
|
||||
{
|
||||
if (isModified())
|
||||
apply();
|
||||
if (isModified()) {
|
||||
apply();
|
||||
}
|
||||
|
||||
setModified(false);
|
||||
setModified(false);
|
||||
}
|
||||
|
||||
/** Discards all changes calling @ref cancel() and resets the modified state. */
|
||||
void PropertyPage::onCancel()
|
||||
{
|
||||
if (isModified())
|
||||
{
|
||||
cancel();
|
||||
setModified(false);
|
||||
}
|
||||
if (isModified()) {
|
||||
cancel();
|
||||
setModified(false);
|
||||
}
|
||||
}
|
||||
|
||||
/** Resets to the default values. */
|
||||
void PropertyPage::onReset()
|
||||
{
|
||||
reset();
|
||||
reset();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
@@ -104,27 +100,23 @@ PreferencePage::PreferencePage(QWidget* parent) : QWidget(parent)
|
||||
{
|
||||
}
|
||||
|
||||
/** Destruction */
|
||||
PreferencePage::~PreferencePage()
|
||||
void PreferencePage::changeEvent(QEvent* event)
|
||||
{
|
||||
}
|
||||
|
||||
void PreferencePage::changeEvent(QEvent *e)
|
||||
{
|
||||
QWidget::changeEvent(e);
|
||||
QWidget::changeEvent(event);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
PreferenceUiForm::PreferenceUiForm(const QString& fn, QWidget* parent)
|
||||
: PreferencePage(parent), form(nullptr)
|
||||
: PreferencePage(parent)
|
||||
, form(nullptr)
|
||||
{
|
||||
UiLoader loader;
|
||||
loader.setLanguageChangeEnabled(true);
|
||||
loader.setWorkingDirectory(QFileInfo(fn).absolutePath());
|
||||
auto loader = UiLoader::newInstance();
|
||||
loader->setWorkingDirectory(QFileInfo(fn).absolutePath());
|
||||
QFile file(fn);
|
||||
if (file.open(QFile::ReadOnly))
|
||||
form = loader.load(&file, this);
|
||||
if (file.open(QFile::ReadOnly)) {
|
||||
form = loader->load(&file, this);
|
||||
}
|
||||
file.close();
|
||||
if (form) {
|
||||
this->setWindowTitle(form->windowTitle());
|
||||
|
||||
@@ -39,9 +39,9 @@ class GuiExport PropertyPage : public QWidget
|
||||
|
||||
public:
|
||||
explicit PropertyPage(QWidget* parent = nullptr);
|
||||
~PropertyPage() override;
|
||||
~PropertyPage() override = default;
|
||||
|
||||
bool isModified();
|
||||
bool isModified() const;
|
||||
void setModified(bool b);
|
||||
void onApply();
|
||||
void onCancel();
|
||||
@@ -69,14 +69,14 @@ class GuiExport PreferencePage : public QWidget
|
||||
|
||||
public:
|
||||
explicit PreferencePage(QWidget* parent = nullptr);
|
||||
~PreferencePage() override;
|
||||
~PreferencePage() override = default;
|
||||
|
||||
public Q_SLOTS:
|
||||
virtual void loadSettings()=0;
|
||||
virtual void saveSettings()=0;
|
||||
|
||||
protected:
|
||||
void changeEvent(QEvent *e) override = 0;
|
||||
void changeEvent(QEvent* event) override = 0;
|
||||
};
|
||||
|
||||
/** Subclass that embeds a form from a UI file.
|
||||
|
||||
@@ -1662,9 +1662,7 @@ QSint--ActionGroup QToolButton::menu-button {
|
||||
QSint--ActionGroup QToolButton#settingsButton,
|
||||
QSint--ActionGroup QToolButton#filterButton,
|
||||
QSint--ActionGroup QToolButton#manualUpdate {
|
||||
padding: 2px;
|
||||
padding-right: 20px; /* make way for the popup button */
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
/* to give widget inside the menu same look as regular menu */
|
||||
|
||||
@@ -1629,9 +1629,7 @@ QSint--ActionGroup QToolButton::menu-button {
|
||||
QSint--ActionGroup QToolButton#settingsButton,
|
||||
QSint--ActionGroup QToolButton#filterButton,
|
||||
QSint--ActionGroup QToolButton#manualUpdate {
|
||||
padding: 2px;
|
||||
padding-right: 20px; /* make way for the popup button */
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
/* to give widget inside the menu same look as regular menu */
|
||||
|
||||
@@ -1629,9 +1629,7 @@ QSint--ActionGroup QToolButton::menu-button {
|
||||
QSint--ActionGroup QToolButton#settingsButton,
|
||||
QSint--ActionGroup QToolButton#filterButton,
|
||||
QSint--ActionGroup QToolButton#manualUpdate {
|
||||
padding: 2px;
|
||||
padding-right: 20px; /* make way for the popup button */
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
/* to give widget inside the menu same look as regular menu */
|
||||
|
||||
@@ -1628,9 +1628,7 @@ QSint--ActionGroup QToolButton::menu-button {
|
||||
QSint--ActionGroup QToolButton#settingsButton,
|
||||
QSint--ActionGroup QToolButton#filterButton,
|
||||
QSint--ActionGroup QToolButton#manualUpdate {
|
||||
padding: 2px;
|
||||
padding-right: 20px; /* make way for the popup button */
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
/* to give widget inside the menu same look as regular menu */
|
||||
|
||||
@@ -1629,9 +1629,7 @@ QSint--ActionGroup QToolButton::menu-button {
|
||||
QSint--ActionGroup QToolButton#settingsButton,
|
||||
QSint--ActionGroup QToolButton#filterButton,
|
||||
QSint--ActionGroup QToolButton#manualUpdate {
|
||||
padding: 2px;
|
||||
padding-right: 20px; /* make way for the popup button */
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
/* to give widget inside the menu same look as regular menu */
|
||||
|
||||
@@ -1629,9 +1629,7 @@ QSint--ActionGroup QToolButton::menu-button {
|
||||
QSint--ActionGroup QToolButton#settingsButton,
|
||||
QSint--ActionGroup QToolButton#filterButton,
|
||||
QSint--ActionGroup QToolButton#manualUpdate {
|
||||
padding: 2px;
|
||||
padding-right: 20px; /* make way for the popup button */
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
/* to give widget inside the menu same look as regular menu */
|
||||
|
||||
@@ -1629,9 +1629,7 @@ QSint--ActionGroup QToolButton::menu-button {
|
||||
QSint--ActionGroup QToolButton#settingsButton,
|
||||
QSint--ActionGroup QToolButton#filterButton,
|
||||
QSint--ActionGroup QToolButton#manualUpdate {
|
||||
padding: 2px;
|
||||
padding-right: 20px; /* make way for the popup button */
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
/* to give widget inside the menu same look as regular menu */
|
||||
|
||||
@@ -1623,9 +1623,7 @@ QSint--ActionGroup QToolButton::menu-button {
|
||||
QSint--ActionGroup QToolButton#settingsButton,
|
||||
QSint--ActionGroup QToolButton#filterButton,
|
||||
QSint--ActionGroup QToolButton#manualUpdate {
|
||||
padding: 2px;
|
||||
padding-right: 20px; /* make way for the popup button */
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
/* to give widget inside the menu same look as regular menu */
|
||||
|
||||
@@ -1626,9 +1626,7 @@ QSint--ActionGroup QToolButton::menu-button {
|
||||
QSint--ActionGroup QToolButton#settingsButton,
|
||||
QSint--ActionGroup QToolButton#filterButton,
|
||||
QSint--ActionGroup QToolButton#manualUpdate {
|
||||
padding: 2px;
|
||||
padding-right: 20px; /* make way for the popup button */
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
/* to give widget inside the menu same look as regular menu */
|
||||
|
||||
@@ -1626,9 +1626,7 @@ QSint--ActionGroup QToolButton::menu-button {
|
||||
QSint--ActionGroup QToolButton#settingsButton,
|
||||
QSint--ActionGroup QToolButton#filterButton,
|
||||
QSint--ActionGroup QToolButton#manualUpdate {
|
||||
padding: 2px;
|
||||
padding-right: 20px; /* make way for the popup button */
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
/* to give widget inside the menu same look as regular menu */
|
||||
|
||||
@@ -1626,9 +1626,7 @@ QSint--ActionGroup QToolButton::menu-button {
|
||||
QSint--ActionGroup QToolButton#settingsButton,
|
||||
QSint--ActionGroup QToolButton#filterButton,
|
||||
QSint--ActionGroup QToolButton#manualUpdate {
|
||||
padding: 2px;
|
||||
padding-right: 20px; /* make way for the popup button */
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
/* to give widget inside the menu same look as regular menu */
|
||||
|
||||
@@ -1815,9 +1815,7 @@ QSint--ActionGroup QToolButton::menu-button {
|
||||
QSint--ActionGroup QToolButton#settingsButton,
|
||||
QSint--ActionGroup QToolButton#filterButton,
|
||||
QSint--ActionGroup QToolButton#manualUpdate {
|
||||
padding: 2px;
|
||||
padding-right: 20px; /* make way for the popup button */
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
/* to give widget inside the menu same look as regular menu */
|
||||
|
||||
@@ -544,8 +544,7 @@ TaskDialogPython::~TaskDialogPython()
|
||||
bool TaskDialogPython::tryLoadUiFile()
|
||||
{
|
||||
if (dlg.hasAttr(std::string("ui"))) {
|
||||
UiLoader loader;
|
||||
loader.setLanguageChangeEnabled(true);
|
||||
auto loader = UiLoader::newInstance();
|
||||
QString fn, icon;
|
||||
Py::String ui(dlg.getAttr(std::string("ui")));
|
||||
std::string path = static_cast<std::string>(ui);
|
||||
@@ -554,7 +553,7 @@ bool TaskDialogPython::tryLoadUiFile()
|
||||
QFile file(fn);
|
||||
QWidget* form = nullptr;
|
||||
if (file.open(QFile::ReadOnly))
|
||||
form = loader.load(&file, nullptr);
|
||||
form = loader->load(&file, nullptr);
|
||||
file.close();
|
||||
if (form) {
|
||||
appendForm(form, QPixmap(icon));
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
# include <QMenu>
|
||||
# include <QMessageBox>
|
||||
# include <QPixmap>
|
||||
# include <QThread>
|
||||
# include <QTimer>
|
||||
# include <QToolTip>
|
||||
# include <QVBoxLayout>
|
||||
@@ -757,6 +758,12 @@ void TreeWidget::updateStatus(bool delay) {
|
||||
}
|
||||
|
||||
void TreeWidget::_updateStatus(bool delay) {
|
||||
// When running from a different thread Qt will raise a warning
|
||||
// when trying to start the QTimer
|
||||
if (Q_UNLIKELY(thread() != QThread::currentThread())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!delay) {
|
||||
if (!ChangedObjects.empty() || !NewObjects.empty())
|
||||
onUpdateStatus();
|
||||
|
||||
+26
-15
@@ -24,6 +24,7 @@
|
||||
#ifndef _PreComp_
|
||||
# include <QAction>
|
||||
# include <QActionGroup>
|
||||
# include <QCoreApplication>
|
||||
# include <QDir>
|
||||
# include <QFile>
|
||||
# include <QLayout>
|
||||
@@ -488,10 +489,20 @@ QString QUiLoader::errorString() const
|
||||
UiLoader::UiLoader(QObject* parent)
|
||||
: QUiLoader(parent)
|
||||
{
|
||||
// do not use the plugins for additional widgets as we don't need them and
|
||||
// the application may crash under Linux (tested on Ubuntu 7.04 & 7.10).
|
||||
clearPluginPaths();
|
||||
this->cw = availableWidgets();
|
||||
setLanguageChangeEnabled(true);
|
||||
}
|
||||
|
||||
std::unique_ptr<UiLoader> UiLoader::newInstance(QObject *parent)
|
||||
{
|
||||
QCoreApplication* app = QCoreApplication::instance();
|
||||
QStringList libPaths = app->libraryPaths();
|
||||
|
||||
app->setLibraryPaths(QStringList{}); //< backup library paths, so QUiLoader won't load plugins by default
|
||||
std::unique_ptr<UiLoader> rv{new UiLoader{parent}};
|
||||
app->setLibraryPaths(libPaths);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
UiLoader::~UiLoader()
|
||||
@@ -544,8 +555,8 @@ void UiLoaderPy::init_type()
|
||||
}
|
||||
|
||||
UiLoaderPy::UiLoaderPy()
|
||||
: loader{UiLoader::newInstance()}
|
||||
{
|
||||
loader.setLanguageChangeEnabled(true);
|
||||
}
|
||||
|
||||
UiLoaderPy::~UiLoaderPy()
|
||||
@@ -592,7 +603,7 @@ Py::Object UiLoaderPy::load(const Py::Tuple& args)
|
||||
}
|
||||
|
||||
if (device) {
|
||||
QWidget* widget = loader.load(device, parent);
|
||||
QWidget* widget = loader->load(device, parent);
|
||||
if (widget) {
|
||||
wrap.loadGuiModule();
|
||||
wrap.loadWidgetsModule();
|
||||
@@ -613,7 +624,7 @@ Py::Object UiLoaderPy::load(const Py::Tuple& args)
|
||||
|
||||
Py::Object UiLoaderPy::createWidget(const Py::Tuple& args)
|
||||
{
|
||||
return wrapFromWidgetFactory(args, std::bind(&UiLoader::createWidget, &loader,
|
||||
return wrapFromWidgetFactory(args, std::bind(&UiLoader::createWidget, loader.get(),
|
||||
std::placeholders::_1,
|
||||
std::placeholders::_2,
|
||||
std::placeholders::_3));
|
||||
@@ -625,7 +636,7 @@ Py::Object UiLoaderPy::addPluginPath(const Py::Tuple& args)
|
||||
if (wrap.loadCoreModule()) {
|
||||
std::string fn;
|
||||
if (wrap.toCString(args[0], fn)) {
|
||||
loader.addPluginPath(QString::fromStdString(fn));
|
||||
loader->addPluginPath(QString::fromStdString(fn));
|
||||
}
|
||||
}
|
||||
return Py::None();
|
||||
@@ -633,13 +644,13 @@ Py::Object UiLoaderPy::addPluginPath(const Py::Tuple& args)
|
||||
|
||||
Py::Object UiLoaderPy::clearPluginPaths(const Py::Tuple& /*args*/)
|
||||
{
|
||||
loader.clearPluginPaths();
|
||||
loader->clearPluginPaths();
|
||||
return Py::None();
|
||||
}
|
||||
|
||||
Py::Object UiLoaderPy::pluginPaths(const Py::Tuple& /*args*/)
|
||||
{
|
||||
auto list = loader.pluginPaths();
|
||||
auto list = loader->pluginPaths();
|
||||
Py::List py;
|
||||
for (const auto& it : list) {
|
||||
py.append(Py::String(it.toStdString()));
|
||||
@@ -649,7 +660,7 @@ Py::Object UiLoaderPy::pluginPaths(const Py::Tuple& /*args*/)
|
||||
|
||||
Py::Object UiLoaderPy::availableWidgets(const Py::Tuple& /*args*/)
|
||||
{
|
||||
auto list = loader.availableWidgets();
|
||||
auto list = loader->availableWidgets();
|
||||
Py::List py;
|
||||
for (const auto& it : list) {
|
||||
py.append(Py::String(it.toStdString()));
|
||||
@@ -665,17 +676,17 @@ Py::Object UiLoaderPy::availableWidgets(const Py::Tuple& /*args*/)
|
||||
|
||||
Py::Object UiLoaderPy::errorString(const Py::Tuple& /*args*/)
|
||||
{
|
||||
return Py::String(loader.errorString().toStdString());
|
||||
return Py::String(loader->errorString().toStdString());
|
||||
}
|
||||
|
||||
Py::Object UiLoaderPy::isLanguageChangeEnabled(const Py::Tuple& /*args*/)
|
||||
{
|
||||
return Py::Boolean(loader.isLanguageChangeEnabled());
|
||||
return Py::Boolean(loader->isLanguageChangeEnabled());
|
||||
}
|
||||
|
||||
Py::Object UiLoaderPy::setLanguageChangeEnabled(const Py::Tuple& args)
|
||||
{
|
||||
loader.setLanguageChangeEnabled(Py::Boolean(args[0]));
|
||||
loader->setLanguageChangeEnabled(Py::Boolean(args[0]));
|
||||
return Py::None();
|
||||
}
|
||||
|
||||
@@ -685,7 +696,7 @@ Py::Object UiLoaderPy::setWorkingDirectory(const Py::Tuple& args)
|
||||
if (wrap.loadCoreModule()) {
|
||||
std::string fn;
|
||||
if (wrap.toCString(args[0], fn)) {
|
||||
loader.setWorkingDirectory(QString::fromStdString(fn));
|
||||
loader->setWorkingDirectory(QString::fromStdString(fn));
|
||||
}
|
||||
}
|
||||
return Py::None();
|
||||
@@ -693,7 +704,7 @@ Py::Object UiLoaderPy::setWorkingDirectory(const Py::Tuple& args)
|
||||
|
||||
Py::Object UiLoaderPy::workingDirectory(const Py::Tuple& /*args*/)
|
||||
{
|
||||
QDir dir = loader.workingDirectory();
|
||||
QDir dir = loader->workingDirectory();
|
||||
QString path = dir.absolutePath();
|
||||
return Py::String(path.toStdString());
|
||||
}
|
||||
|
||||
+24
-2
@@ -34,6 +34,7 @@
|
||||
#endif
|
||||
|
||||
#include <CXX/Extensions.hxx>
|
||||
#include <memory>
|
||||
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
@@ -106,8 +107,29 @@ private:
|
||||
*/
|
||||
class UiLoader : public QUiLoader
|
||||
{
|
||||
public:
|
||||
protected:
|
||||
/**
|
||||
* A protected construct for UiLoader.
|
||||
* To create an instance of UiLoader @see UiLoader::newInstance()
|
||||
*/
|
||||
explicit UiLoader(QObject* parent=nullptr);
|
||||
|
||||
public:
|
||||
/**
|
||||
* Creates a new instance of a UiLoader.
|
||||
*
|
||||
* Due to its flaw the QUiLoader upon creation loads every available Qt
|
||||
* designer plugin it can find in QApplication::libraryPaths(). Some of
|
||||
* those plugins may perform some unexpected actions upon load which may
|
||||
* interfere with FreeCAD's functionality. Only way to avoid such behaviour
|
||||
* is to reset QApplication::libraryPaths, create a QUiLoader and then
|
||||
* restore the libs paths. Hence need for this function to wrap
|
||||
* construction.
|
||||
*
|
||||
* @see https://github.com/FreeCAD/FreeCAD/issues/8708
|
||||
*/
|
||||
static std::unique_ptr<UiLoader> newInstance(QObject *parent=0);
|
||||
|
||||
~UiLoader() override;
|
||||
|
||||
/**
|
||||
@@ -149,7 +171,7 @@ private:
|
||||
static PyObject *PyMake(struct _typeobject *, PyObject *, PyObject *);
|
||||
|
||||
private:
|
||||
UiLoader loader;
|
||||
std::unique_ptr<UiLoader> loader;
|
||||
};
|
||||
|
||||
} // namespace Gui
|
||||
|
||||
@@ -461,11 +461,10 @@ void PyResource::load(const char* name)
|
||||
|
||||
QWidget* w=nullptr;
|
||||
try {
|
||||
UiLoader loader;
|
||||
loader.setLanguageChangeEnabled(true);
|
||||
auto loader = UiLoader::newInstance();
|
||||
QFile file(fn);
|
||||
if (file.open(QFile::ReadOnly))
|
||||
w = loader.load(&file, QApplication::activeWindow());
|
||||
w = loader->load(&file, QApplication::activeWindow());
|
||||
file.close();
|
||||
}
|
||||
catch (...) {
|
||||
|
||||
@@ -78,6 +78,7 @@ SET(FemExamples_SRCS
|
||||
femexamples/equation_electrostatics_capacitance_two_balls.py
|
||||
femexamples/equation_electrostatics_electricforce_elmer_nongui6.py
|
||||
femexamples/equation_flow_elmer_2D.py
|
||||
femexamples/equation_flow_initial_elmer_2D.py
|
||||
femexamples/equation_flow_turbulent_elmer_2D.py
|
||||
femexamples/equation_flux_elmer.py
|
||||
femexamples/equation_magnetodynamics_elmer.py
|
||||
|
||||
@@ -6,78 +6,27 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
<width>300</width>
|
||||
<height>197</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Constraint Properties</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="volocityXLbl">
|
||||
<property name="text">
|
||||
<string>Velocity x:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="velocityYLbl">
|
||||
<property name="text">
|
||||
<string>Velocity y:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="velocityZLbl">
|
||||
<property name="text">
|
||||
<string>Velocity z:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="Gui::InputField" name="velocityZTxt">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="1" column="3">
|
||||
<widget class="QCheckBox" name="formulaXCB">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>1.000000000000000</double>
|
||||
</property>
|
||||
<property name="unit" stdset="0">
|
||||
<string>m/s</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="velocityZBox">
|
||||
<property name="text">
|
||||
<string>unspecified</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
<string>formula</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="Gui::InputField" name="velocityXTxt">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>1.000000000000000</double>
|
||||
</property>
|
||||
<property name="unit" stdset="0">
|
||||
<string>m/s</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<item row="0" column="3">
|
||||
<widget class="QCheckBox" name="velocityXBox">
|
||||
<property name="text">
|
||||
<string>unspecified</string>
|
||||
@@ -87,24 +36,52 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="Gui::InputField" name="velocityYTxt">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>1.000000000000000</double>
|
||||
</property>
|
||||
<property name="unit" stdset="0">
|
||||
<string>m/s</string>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="volocityXLbl">
|
||||
<property name="text">
|
||||
<string>Velocity x:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="formulaX">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="Gui::QuantitySpinBox" name="velocityX">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="unit" stdset="0">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="1" column="3">
|
||||
<widget class="QCheckBox" name="formulaYCB">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>formula</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="velocityYLbl">
|
||||
<property name="text">
|
||||
<string>Velocity y:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="3">
|
||||
<widget class="QCheckBox" name="velocityYBox">
|
||||
<property name="text">
|
||||
<string>unspecified</string>
|
||||
@@ -114,12 +91,77 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="formulaY">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="Gui::QuantitySpinBox" name="velocityY">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="unit" stdset="0">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="6" column="0" colspan="2">
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="0" column="3">
|
||||
<widget class="QCheckBox" name="velocityZBox">
|
||||
<property name="text">
|
||||
<string>unspecified</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="3">
|
||||
<widget class="QCheckBox" name="formulaZCB">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>formula</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="velocityZLbl">
|
||||
<property name="text">
|
||||
<string>Velocity z:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="formulaZ">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="Gui::QuantitySpinBox" name="velocityZ">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="unit" stdset="0">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="normalBox">
|
||||
<property name="text">
|
||||
<string>normal to boundary</string>
|
||||
<string>Normal to boundary</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -127,9 +169,9 @@
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>Gui::InputField</class>
|
||||
<extends>QLineEdit</extends>
|
||||
<header>Gui/InputField.h</header>
|
||||
<class>Gui::QuantitySpinBox</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>Gui/QuantitySpinBox.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
@@ -137,96 +179,48 @@
|
||||
<connection>
|
||||
<sender>velocityXBox</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>velocityXTxt</receiver>
|
||||
<slot>setEnabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>230</x>
|
||||
<y>44</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>230</x>
|
||||
<y>18</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>velocityXBox</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>velocityXTxt</receiver>
|
||||
<receiver>formulaXCB</receiver>
|
||||
<slot>setDisabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>230</x>
|
||||
<y>44</y>
|
||||
<x>351</x>
|
||||
<y>19</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>230</x>
|
||||
<y>18</y>
|
||||
<x>351</x>
|
||||
<y>45</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>velocityYBox</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>velocityYTxt</receiver>
|
||||
<slot>setEnabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>347</x>
|
||||
<y>53</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>184</x>
|
||||
<y>53</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>velocityYBox</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>velocityYTxt</receiver>
|
||||
<receiver>formulaYCB</receiver>
|
||||
<slot>setDisabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>347</x>
|
||||
<y>53</y>
|
||||
<x>351</x>
|
||||
<y>73</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>184</x>
|
||||
<y>53</y>
|
||||
<x>351</x>
|
||||
<y>99</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>velocityZBox</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>velocityZTxt</receiver>
|
||||
<slot>setEnabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>347</x>
|
||||
<y>87</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>184</x>
|
||||
<y>87</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>velocityZBox</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>velocityZTxt</receiver>
|
||||
<receiver>formulaZCB</receiver>
|
||||
<slot>setDisabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>347</x>
|
||||
<y>87</y>
|
||||
<x>351</x>
|
||||
<y>127</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>184</x>
|
||||
<y>87</y>
|
||||
<x>351</x>
|
||||
<y>153</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
|
||||
@@ -6,40 +6,27 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
<width>300</width>
|
||||
<height>174</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Constraint Properties</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<property name="fieldGrowthPolicy">
|
||||
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
|
||||
</property>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="volocityXLbl">
|
||||
<property name="text">
|
||||
<string>Velocity x:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="Gui::InputField" name="velocityXTxt">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="1" column="3">
|
||||
<widget class="QCheckBox" name="formulaXCB">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>1.000000000000000</double>
|
||||
</property>
|
||||
<property name="unit" stdset="0">
|
||||
<string>m/s</string>
|
||||
<property name="text">
|
||||
<string>formula</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<item row="0" column="3">
|
||||
<widget class="QCheckBox" name="velocityXBox">
|
||||
<property name="text">
|
||||
<string>unspecified</string>
|
||||
@@ -49,31 +36,52 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="velocityYLbl">
|
||||
<property name="text">
|
||||
<string>Velocity y:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="Gui::InputField" name="velocityYTxt">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>1.000000000000000</double>
|
||||
</property>
|
||||
<property name="unit" stdset="0">
|
||||
<string>m/s</string>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="volocityXLbl">
|
||||
<property name="text">
|
||||
<string>Velocity x:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="formulaX">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="Gui::QuantitySpinBox" name="velocityX">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="unit" stdset="0">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="1" column="3">
|
||||
<widget class="QCheckBox" name="formulaYCB">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>formula</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="velocityYLbl">
|
||||
<property name="text">
|
||||
<string>Velocity y:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="3">
|
||||
<widget class="QCheckBox" name="velocityYBox">
|
||||
<property name="text">
|
||||
<string>unspecified</string>
|
||||
@@ -83,31 +91,28 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="velocityZLbl">
|
||||
<property name="text">
|
||||
<string>Velocity z:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="Gui::InputField" name="velocityZTxt">
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="formulaY">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>1.000000000000000</double>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="Gui::QuantitySpinBox" name="velocityY">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="unit" stdset="0">
|
||||
<string>m/s</string>
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="0" column="3">
|
||||
<widget class="QCheckBox" name="velocityZBox">
|
||||
<property name="text">
|
||||
<string>unspecified</string>
|
||||
@@ -117,15 +122,49 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="3">
|
||||
<widget class="QCheckBox" name="formulaZCB">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>formula</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="velocityZLbl">
|
||||
<property name="text">
|
||||
<string>Velocity z:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="formulaZ">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="Gui::QuantitySpinBox" name="velocityZ">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="unit" stdset="0">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>Gui::InputField</class>
|
||||
<extends>QLineEdit</extends>
|
||||
<header>Gui/InputField.h</header>
|
||||
<class>Gui::QuantitySpinBox</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>Gui/QuantitySpinBox.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
@@ -133,96 +172,48 @@
|
||||
<connection>
|
||||
<sender>velocityXBox</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>velocityXTxt</receiver>
|
||||
<slot>setEnabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>230</x>
|
||||
<y>44</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>230</x>
|
||||
<y>18</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>velocityXBox</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>velocityXTxt</receiver>
|
||||
<receiver>formulaXCB</receiver>
|
||||
<slot>setDisabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>230</x>
|
||||
<y>44</y>
|
||||
<x>351</x>
|
||||
<y>19</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>230</x>
|
||||
<y>18</y>
|
||||
<x>351</x>
|
||||
<y>45</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>velocityYBox</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>velocityYTxt</receiver>
|
||||
<slot>setEnabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>347</x>
|
||||
<y>53</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>184</x>
|
||||
<y>53</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>velocityYBox</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>velocityYTxt</receiver>
|
||||
<receiver>formulaYCB</receiver>
|
||||
<slot>setDisabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>347</x>
|
||||
<y>53</y>
|
||||
<x>351</x>
|
||||
<y>73</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>184</x>
|
||||
<y>53</y>
|
||||
<x>351</x>
|
||||
<y>99</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>velocityZBox</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>velocityZTxt</receiver>
|
||||
<slot>setEnabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>347</x>
|
||||
<y>87</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>184</x>
|
||||
<y>87</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>velocityZBox</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>velocityZTxt</receiver>
|
||||
<receiver>formulaZCB</receiver>
|
||||
<slot>setDisabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>347</x>
|
||||
<y>87</y>
|
||||
<x>351</x>
|
||||
<y>127</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>184</x>
|
||||
<y>87</y>
|
||||
<x>351</x>
|
||||
<y>153</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
|
||||
@@ -40,7 +40,7 @@ def get_information():
|
||||
"name": "Flow - Elmer 2D",
|
||||
"meshtype": "solid",
|
||||
"meshelement": "Tet10",
|
||||
"constraints": ["initial pressure", "initial temperature", "initial velocity",
|
||||
"constraints": ["initial pressure", "initial temperature",
|
||||
"temperature", "velocity"],
|
||||
"solvers": ["elmer"],
|
||||
"material": "fluid",
|
||||
@@ -71,19 +71,19 @@ def setup(doc=None, solvertype="elmer"):
|
||||
# geometric objects
|
||||
|
||||
# the wire defining the pipe volume in 2D
|
||||
p1 = Vector(400, 0, -50.000)
|
||||
p2 = Vector(400, 0, -150.000)
|
||||
p3 = Vector(1200, 0, -150.000)
|
||||
p4 = Vector(1200, 0, 50.000)
|
||||
p5 = Vector(0, 0, 50.000)
|
||||
p6 = Vector(0, 0, -50.000)
|
||||
p1 = Vector(400, -50.000, 0)
|
||||
p2 = Vector(400, -150.000, 0)
|
||||
p3 = Vector(1200, -150.000, 0)
|
||||
p4 = Vector(1200, 50.000, 0)
|
||||
p5 = Vector(0, 50.000, 0)
|
||||
p6 = Vector(0, -50.000, 0)
|
||||
wire = Draft.make_wire([p1, p2, p3, p4, p5, p6], closed=True)
|
||||
wire.Label = "Wire"
|
||||
|
||||
# the circle defining the heating rod
|
||||
pCirc = Vector(160, 0, 0)
|
||||
axisCirc = Vector(1, 0, 0)
|
||||
placementCircle = Placement(pCirc, Rotation(axisCirc, 90))
|
||||
placementCircle = Placement(pCirc, Rotation(axisCirc, 0))
|
||||
circle = Draft.make_circle(10, placement=placementCircle)
|
||||
circle.Label = "HeatingRod"
|
||||
circle.ViewObject.Visibility = False
|
||||
@@ -107,7 +107,6 @@ def setup(doc=None, solvertype="elmer"):
|
||||
doc.recompute()
|
||||
if FreeCAD.GuiUp:
|
||||
BooleanFragments.ViewObject.Transparency = 50
|
||||
BooleanFragments.ViewObject.Document.activeView().viewFront()
|
||||
BooleanFragments.ViewObject.Document.activeView().fitAll()
|
||||
|
||||
# analysis
|
||||
@@ -119,6 +118,7 @@ def setup(doc=None, solvertype="elmer"):
|
||||
# solver
|
||||
if solvertype == "elmer":
|
||||
solver_obj = ObjectsFem.makeSolverElmer(doc, "SolverElmer")
|
||||
solver_obj.CoordinateSystem = "Cartesian 2D"
|
||||
equation_flow = ObjectsFem.makeEquationFlow(doc, solver_obj)
|
||||
equation_heat = ObjectsFem.makeEquationHeat(doc, solver_obj)
|
||||
else:
|
||||
@@ -133,6 +133,7 @@ def setup(doc=None, solvertype="elmer"):
|
||||
equation_flow.IdrsParameter = 3
|
||||
equation_flow.LinearIterativeMethod = "Idrs"
|
||||
equation_flow.LinearPreconditioning = "ILU1"
|
||||
equation_flow.Variable = "Flow Solution[Velocity:2 Pressure:1]"
|
||||
equation_heat.Convection = "Computed"
|
||||
equation_heat.IdrsParameter = 3
|
||||
equation_heat.LinearIterativeMethod = "Idrs"
|
||||
@@ -178,21 +179,12 @@ def setup(doc=None, solvertype="elmer"):
|
||||
# constraint inlet velocity
|
||||
FlowVelocity_Inlet = ObjectsFem.makeConstraintFlowVelocity(doc, "FlowVelocity_Inlet")
|
||||
FlowVelocity_Inlet.References = [(BooleanFragments, "Edge5")]
|
||||
FlowVelocity_Inlet.NormalDirection = Vector(-1, 0, 0)
|
||||
FlowVelocity_Inlet.VelocityX = 0.020
|
||||
FlowVelocity_Inlet.VelocityXEnabled = True
|
||||
FlowVelocity_Inlet.VelocityYEnabled = True
|
||||
FlowVelocity_Inlet.VelocityZEnabled = True
|
||||
FlowVelocity_Inlet.VelocityXFormula = "Variable Coordinate 2; Real MATC \"10*(tx+50e-3)*(50e-3-tx)\""
|
||||
FlowVelocity_Inlet.VelocityXUnspecified = False
|
||||
FlowVelocity_Inlet.VelocityXHasFormula = True
|
||||
FlowVelocity_Inlet.VelocityYUnspecified = False
|
||||
analysis.addObject(FlowVelocity_Inlet)
|
||||
|
||||
# constraint outlet velocity
|
||||
FlowVelocity_Outlet = ObjectsFem.makeConstraintFlowVelocity(doc, "FlowVelocity_Outlet")
|
||||
FlowVelocity_Outlet.References = [(BooleanFragments, "Edge6")]
|
||||
FlowVelocity_Outlet.NormalDirection = Vector(1, 0, 0)
|
||||
FlowVelocity_Outlet.VelocityYEnabled = True
|
||||
FlowVelocity_Outlet.VelocityZEnabled = True
|
||||
analysis.addObject(FlowVelocity_Outlet)
|
||||
|
||||
# constraint wall velocity
|
||||
FlowVelocity_Wall = ObjectsFem.makeConstraintFlowVelocity(doc, "FlowVelocity_Wall")
|
||||
FlowVelocity_Wall.References = [
|
||||
@@ -200,21 +192,10 @@ def setup(doc=None, solvertype="elmer"):
|
||||
(BooleanFragments, "Edge3"),
|
||||
(BooleanFragments, "Edge4"),
|
||||
(BooleanFragments, "Edge7")]
|
||||
FlowVelocity_Wall.NormalDirection = Vector(0, 0, -1)
|
||||
FlowVelocity_Wall.VelocityXEnabled = True
|
||||
FlowVelocity_Wall.VelocityYEnabled = True
|
||||
FlowVelocity_Wall.VelocityZEnabled = True
|
||||
FlowVelocity_Wall.VelocityXUnspecified = False
|
||||
FlowVelocity_Wall.VelocityYUnspecified = False
|
||||
analysis.addObject(FlowVelocity_Wall)
|
||||
|
||||
# constraint initial velocity
|
||||
FlowVelocity_Initial = ObjectsFem.makeConstraintInitialFlowVelocity(doc, "FlowVelocity_Initial")
|
||||
FlowVelocity_Initial.References = [(BooleanFragments, "Face2")]
|
||||
FlowVelocity_Initial.NormalDirection = Vector(0, -1, 0)
|
||||
FlowVelocity_Initial.VelocityXEnabled = True
|
||||
FlowVelocity_Initial.VelocityYEnabled = True
|
||||
FlowVelocity_Initial.VelocityZEnabled = True
|
||||
analysis.addObject(FlowVelocity_Initial)
|
||||
|
||||
# constraint initial temperature
|
||||
Temperature_Initial = ObjectsFem.makeConstraintInitialTemperature(doc, "Temperature_Initial")
|
||||
Temperature_Initial.initialTemperature = 300.0
|
||||
@@ -233,7 +214,7 @@ def setup(doc=None, solvertype="elmer"):
|
||||
|
||||
# constraint inlet temperature
|
||||
Temperature_Inlet = ObjectsFem.makeConstraintTemperature(doc, "Temperature_Inlet")
|
||||
Temperature_Inlet.Temperature = 350.0
|
||||
Temperature_Inlet.Temperature = 300.0
|
||||
Temperature_Inlet.NormalDirection = Vector(-1, 0, 0)
|
||||
Temperature_Inlet.References = [(BooleanFragments, "Edge5")]
|
||||
analysis.addObject(Temperature_Inlet)
|
||||
|
||||
@@ -0,0 +1,275 @@
|
||||
# ***************************************************************************
|
||||
# * Copyright (c) 2023 Uwe Stöhr <[email protected]> *
|
||||
# * *
|
||||
# * This file is part of the FreeCAD CAx development system. *
|
||||
# * *
|
||||
# * This program is free software; you can redistribute it and/or modify *
|
||||
# * it under the terms of the GNU Lesser General Public License (LGPL) *
|
||||
# * as published by the Free Software Foundation; either version 2 of *
|
||||
# * the License, or (at your option) any later version. *
|
||||
# * for detail see the LICENCE text file. *
|
||||
# * *
|
||||
# * This program is distributed in the hope that it will be useful, *
|
||||
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
# * GNU Library General Public License for more details. *
|
||||
# * *
|
||||
# * You should have received a copy of the GNU Library General Public *
|
||||
# * License along with this program; if not, write to the Free Software *
|
||||
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
||||
# * USA *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
import sys
|
||||
import FreeCAD
|
||||
from FreeCAD import Placement
|
||||
from FreeCAD import Rotation
|
||||
from FreeCAD import Vector
|
||||
|
||||
import Draft
|
||||
import ObjectsFem
|
||||
|
||||
from BOPTools import SplitFeatures
|
||||
from . import manager
|
||||
from .manager import get_meshname
|
||||
from .manager import init_doc
|
||||
|
||||
def get_information():
|
||||
return {
|
||||
"name": "Initial Flow - Elmer 2D",
|
||||
"meshtype": "solid",
|
||||
"meshelement": "Tet10",
|
||||
"constraints": ["initial pressure", "initial temperature", "initial velocity",
|
||||
"temperature", "velocity"],
|
||||
"solvers": ["elmer"],
|
||||
"material": "fluid",
|
||||
"equations": ["flow", "heat"]
|
||||
}
|
||||
|
||||
def get_explanation(header=""):
|
||||
return header + """
|
||||
|
||||
To run the example from Python console use:
|
||||
from femexamples.equation_flow_initial_elmer_2D import setup
|
||||
setup()
|
||||
|
||||
Flow and Heat equation with initial velocity - Elmer solver
|
||||
|
||||
"""
|
||||
|
||||
def setup(doc=None, solvertype="elmer"):
|
||||
|
||||
# init FreeCAD document
|
||||
if doc is None:
|
||||
doc = init_doc()
|
||||
|
||||
# explanation object
|
||||
# just keep the following line and change text string in get_explanation method
|
||||
manager.add_explanation_obj(doc, get_explanation(manager.get_header(get_information())))
|
||||
|
||||
# geometric objects
|
||||
|
||||
# the wire defining the pipe volume in 2D
|
||||
p1 = Vector(400, -50.000, 0)
|
||||
p2 = Vector(400, -150.000, 0)
|
||||
p3 = Vector(1200, -150.000, 0)
|
||||
p4 = Vector(1200, 50.000, 0)
|
||||
p5 = Vector(0, 50.000, 0)
|
||||
p6 = Vector(0, -50.000, 0)
|
||||
wire = Draft.make_wire([p1, p2, p3, p4, p5, p6], closed=True)
|
||||
wire.Label = "Wire"
|
||||
|
||||
# the circle defining the heating rod
|
||||
pCirc = Vector(160, 0, 0)
|
||||
axisCirc = Vector(1, 0, 0)
|
||||
placementCircle = Placement(pCirc, Rotation(axisCirc, 0))
|
||||
circle = Draft.make_circle(10, placement=placementCircle)
|
||||
circle.Label = "HeatingRod"
|
||||
circle.ViewObject.Visibility = False
|
||||
|
||||
# a link of the circle
|
||||
circleLink = doc.addObject("App::Link", "Link-HeatingRod")
|
||||
circleLink.LinkTransform = True
|
||||
circleLink.LinkedObject = circle
|
||||
|
||||
# cut rod from wire to get volume of fluid
|
||||
cut = doc.addObject("Part::Cut", "Cut")
|
||||
cut.Base = wire
|
||||
cut.Tool = circleLink
|
||||
cut.ViewObject.Visibility = False
|
||||
|
||||
# BooleanFregments object to combine cut with rod
|
||||
BooleanFragments = SplitFeatures.makeBooleanFragments(name="BooleanFragments")
|
||||
BooleanFragments.Objects = [cut, circle]
|
||||
|
||||
# set view
|
||||
doc.recompute()
|
||||
if FreeCAD.GuiUp:
|
||||
BooleanFragments.ViewObject.Transparency = 50
|
||||
BooleanFragments.ViewObject.Document.activeView().fitAll()
|
||||
|
||||
# analysis
|
||||
analysis = ObjectsFem.makeAnalysis(doc, "Analysis")
|
||||
if FreeCAD.GuiUp:
|
||||
import FemGui
|
||||
FemGui.setActiveAnalysis(analysis)
|
||||
|
||||
# solver
|
||||
if solvertype == "elmer":
|
||||
solver_obj = ObjectsFem.makeSolverElmer(doc, "SolverElmer")
|
||||
solver_obj.CoordinateSystem = "Cartesian 2D"
|
||||
equation_flow = ObjectsFem.makeEquationFlow(doc, solver_obj)
|
||||
equation_heat = ObjectsFem.makeEquationHeat(doc, solver_obj)
|
||||
else:
|
||||
FreeCAD.Console.PrintWarning(
|
||||
"Unknown or unsupported solver type: {}. "
|
||||
"No solver object was created.\n".format(solvertype)
|
||||
)
|
||||
return doc
|
||||
analysis.addObject(solver_obj)
|
||||
|
||||
# solver settings
|
||||
equation_flow.IdrsParameter = 3
|
||||
equation_flow.LinearIterativeMethod = "Idrs"
|
||||
equation_flow.LinearPreconditioning = "ILU1"
|
||||
equation_flow.NonlinearIterations = 20
|
||||
equation_flow.NonlinearNewtonAfterIterations = 20
|
||||
equation_flow.RelaxationFactor = 0.15
|
||||
equation_flow.Variable = "Flow Solution[Velocity:2 Pressure:1]"
|
||||
equation_heat.Convection = "Computed"
|
||||
equation_heat.IdrsParameter = 3
|
||||
equation_heat.LinearIterativeMethod = "Idrs"
|
||||
equation_heat.LinearPreconditioning = "ILU1"
|
||||
equation_heat.NonlinearIterations = 20
|
||||
equation_heat.NonlinearNewtonAfterIterations = 20
|
||||
equation_heat.Priority = 5
|
||||
equation_heat.RelaxationFactor = 0.15
|
||||
equation_heat.Stabilize = True
|
||||
|
||||
# material
|
||||
|
||||
# fluid
|
||||
material_obj = ObjectsFem.makeMaterialFluid(doc, "Material_Fluid")
|
||||
mat = material_obj.Material
|
||||
mat["Name"] = "Carbon dioxide"
|
||||
mat["Density"] = "1.8393 kg/m^3"
|
||||
mat["DynamicViscosity"] = "14.7e-6 kg/m/s"
|
||||
mat["ThermalConductivity"] = "0.016242 W/m/K"
|
||||
mat["ThermalExpansionCoefficient"] = "0.00343 m/m/K"
|
||||
mat["SpecificHeat"] = "0.846 kJ/kg/K"
|
||||
material_obj.Material = mat
|
||||
material_obj.References = [(BooleanFragments, "Face2")]
|
||||
analysis.addObject(material_obj)
|
||||
|
||||
# tube wall
|
||||
material_obj = ObjectsFem.makeMaterialSolid(doc, "Material_Wall")
|
||||
mat = material_obj.Material
|
||||
mat["Name"] = "Aluminum Generic"
|
||||
mat["Density"] = "2700 kg/m^3"
|
||||
mat["PoissonRatio"] = "0.35"
|
||||
mat["ShearModulus"] = "25.0 GPa"
|
||||
mat["UltimateTensileStrength"] = "310 MPa"
|
||||
mat["YoungsModulus"] = "70000 MPa"
|
||||
mat["ThermalConductivity"] = "237.0 W/m/K"
|
||||
mat["ThermalExpansionCoefficient"] = "23.1 µm/m/K"
|
||||
mat["SpecificHeat"] = "897.0 J/kg/K"
|
||||
material_obj.Material = mat
|
||||
material_obj.References = [(BooleanFragments, "Face1")]
|
||||
analysis.addObject(material_obj)
|
||||
|
||||
# constraint inlet velocity
|
||||
FlowVelocity_Inlet = ObjectsFem.makeConstraintFlowVelocity(doc, "FlowVelocity_Inlet")
|
||||
FlowVelocity_Inlet.References = [(BooleanFragments, "Edge5")]
|
||||
FlowVelocity_Inlet.VelocityX = "20.0 mm/s"
|
||||
FlowVelocity_Inlet.VelocityXUnspecified = False
|
||||
analysis.addObject(FlowVelocity_Inlet)
|
||||
|
||||
# constraint wall velocity
|
||||
FlowVelocity_Wall = ObjectsFem.makeConstraintFlowVelocity(doc, "FlowVelocity_Wall")
|
||||
FlowVelocity_Wall.References = [
|
||||
(BooleanFragments, "Edge2"),
|
||||
(BooleanFragments, "Edge3"),
|
||||
(BooleanFragments, "Edge4"),
|
||||
(BooleanFragments, "Edge7")]
|
||||
FlowVelocity_Wall.VelocityXUnspecified = False
|
||||
FlowVelocity_Wall.VelocityYUnspecified = False
|
||||
analysis.addObject(FlowVelocity_Wall)
|
||||
|
||||
# constraint initial velocity
|
||||
FlowVelocity_Initial = ObjectsFem.makeConstraintInitialFlowVelocity(doc, "FlowVelocity_Initial")
|
||||
FlowVelocity_Initial.References = [(BooleanFragments, "Face2")]
|
||||
FlowVelocity_Initial.VelocityX = "20.0 mm/s"
|
||||
FlowVelocity_Initial.VelocityY = "-20.0 mm/s"
|
||||
FlowVelocity_Initial.VelocityXUnspecified = False
|
||||
FlowVelocity_Initial.VelocityYUnspecified = False
|
||||
analysis.addObject(FlowVelocity_Initial)
|
||||
|
||||
# constraint initial temperature
|
||||
Temperature_Initial = ObjectsFem.makeConstraintInitialTemperature(doc, "Temperature_Initial")
|
||||
Temperature_Initial.initialTemperature = 300.0
|
||||
analysis.addObject(Temperature_Initial)
|
||||
|
||||
# constraint wall temperature
|
||||
Temperature_Wall = ObjectsFem.makeConstraintTemperature(doc, "Temperature_Wall")
|
||||
Temperature_Wall.Temperature = 300.0
|
||||
Temperature_Wall.NormalDirection = Vector(0, 0, -1)
|
||||
Temperature_Wall.References = [
|
||||
(BooleanFragments, "Edge2"),
|
||||
(BooleanFragments, "Edge3"),
|
||||
(BooleanFragments, "Edge4"),
|
||||
(BooleanFragments, "Edge7")]
|
||||
analysis.addObject(Temperature_Wall)
|
||||
|
||||
# constraint inlet temperature
|
||||
Temperature_Inlet = ObjectsFem.makeConstraintTemperature(doc, "Temperature_Inlet")
|
||||
Temperature_Inlet.Temperature = 300.0
|
||||
Temperature_Inlet.NormalDirection = Vector(-1, 0, 0)
|
||||
Temperature_Inlet.References = [(BooleanFragments, "Edge5")]
|
||||
analysis.addObject(Temperature_Inlet)
|
||||
|
||||
# constraint heating rod temperature
|
||||
Temperature_HeatingRod = ObjectsFem.makeConstraintTemperature(doc, "Temperature_HeatingRod")
|
||||
Temperature_HeatingRod.Temperature = 373.0
|
||||
Temperature_HeatingRod.NormalDirection = Vector(0, -1, 0)
|
||||
Temperature_HeatingRod.References = [(BooleanFragments, "Edge1")]
|
||||
analysis.addObject(Temperature_HeatingRod)
|
||||
|
||||
# constraint initial pressure
|
||||
Pressure_Initial = ObjectsFem.makeConstraintInitialPressure(doc, "Pressure_Initial")
|
||||
Pressure_Initial.Pressure = "100.0 kPa"
|
||||
Pressure_Initial.NormalDirection = Vector(0, -1, 0)
|
||||
Pressure_Initial.References = [(BooleanFragments, "Face2")]
|
||||
analysis.addObject(Pressure_Initial)
|
||||
|
||||
# mesh
|
||||
femmesh_obj = analysis.addObject(ObjectsFem.makeMeshGmsh(doc, get_meshname()))[0]
|
||||
femmesh_obj.Part = BooleanFragments
|
||||
femmesh_obj.ElementOrder = "1st"
|
||||
femmesh_obj.CharacteristicLengthMax = "4 mm"
|
||||
femmesh_obj.ViewObject.Visibility = False
|
||||
|
||||
# mesh_region
|
||||
mesh_region = ObjectsFem.makeMeshRegion(doc, femmesh_obj, name="MeshRegion")
|
||||
mesh_region.CharacteristicLength = "2 mm"
|
||||
mesh_region.References = [
|
||||
(BooleanFragments, "Edge1"),
|
||||
(BooleanFragments, "Vertex2"),
|
||||
(BooleanFragments, "Vertex4"),
|
||||
(BooleanFragments, "Vertex6")]
|
||||
mesh_region.ViewObject.Visibility = False
|
||||
|
||||
# generate the mesh
|
||||
from femmesh import gmshtools
|
||||
gmsh_mesh = gmshtools.GmshTools(femmesh_obj, analysis)
|
||||
try:
|
||||
error = gmsh_mesh.create_mesh()
|
||||
except Exception:
|
||||
error = sys.exc_info()[1]
|
||||
FreeCAD.Console.PrintError(
|
||||
"Unexpected error when creating mesh: {}\n"
|
||||
.format(error)
|
||||
)
|
||||
|
||||
doc.recompute()
|
||||
return doc
|
||||
@@ -40,7 +40,7 @@ def get_information():
|
||||
"name": "Turbulent Flow - Elmer 2D",
|
||||
"meshtype": "solid",
|
||||
"meshelement": "Tet10",
|
||||
"constraints": ["initial pressure", "initial temperature", "initial velocity",
|
||||
"constraints": ["initial pressure", "initial temperature",
|
||||
"temperature", "velocity"],
|
||||
"solvers": ["elmer"],
|
||||
"material": "fluid",
|
||||
@@ -54,7 +54,7 @@ To run the example from Python console use:
|
||||
from femexamples.equation_flow_turbulent_elmer_2D import setup
|
||||
setup()
|
||||
|
||||
Flow and Heat equation - Elmer solver
|
||||
Flow and Heat equation in turbulent flow - Elmer solver
|
||||
|
||||
"""
|
||||
|
||||
@@ -71,19 +71,19 @@ def setup(doc=None, solvertype="elmer"):
|
||||
# geometric objects
|
||||
|
||||
# the wire defining the pipe volume in 2D
|
||||
p1 = Vector(400, 0, -50.000)
|
||||
p2 = Vector(400, 0, -150.000)
|
||||
p3 = Vector(1200, 0, -150.000)
|
||||
p4 = Vector(1200, 0, 50.000)
|
||||
p5 = Vector(0, 0, 50.000)
|
||||
p6 = Vector(0, 0, -50.000)
|
||||
p1 = Vector(400, -50.000, 0)
|
||||
p2 = Vector(400, -150.000, 0)
|
||||
p3 = Vector(1200, -150.000, 0)
|
||||
p4 = Vector(1200, 50.000, 0)
|
||||
p5 = Vector(0, 50.000, 0)
|
||||
p6 = Vector(0, -50.000, 0)
|
||||
wire = Draft.make_wire([p1, p2, p3, p4, p5, p6], closed=True)
|
||||
wire.Label = "Wire"
|
||||
|
||||
# the circle defining the heating rod
|
||||
pCirc = Vector(160, 0, 0)
|
||||
axisCirc = Vector(1, 0, 0)
|
||||
placementCircle = Placement(pCirc, Rotation(axisCirc, 90))
|
||||
placementCircle = Placement(pCirc, Rotation(axisCirc, 0))
|
||||
circle = Draft.make_circle(10, placement=placementCircle)
|
||||
circle.Label = "HeatingRod"
|
||||
circle.ViewObject.Visibility = False
|
||||
@@ -107,7 +107,6 @@ def setup(doc=None, solvertype="elmer"):
|
||||
doc.recompute()
|
||||
if FreeCAD.GuiUp:
|
||||
BooleanFragments.ViewObject.Transparency = 50
|
||||
BooleanFragments.ViewObject.Document.activeView().viewFront()
|
||||
BooleanFragments.ViewObject.Document.activeView().fitAll()
|
||||
|
||||
# analysis
|
||||
@@ -119,6 +118,7 @@ def setup(doc=None, solvertype="elmer"):
|
||||
# solver
|
||||
if solvertype == "elmer":
|
||||
solver_obj = ObjectsFem.makeSolverElmer(doc, "SolverElmer")
|
||||
solver_obj.CoordinateSystem = "Cartesian 2D"
|
||||
equation_flow = ObjectsFem.makeEquationFlow(doc, solver_obj)
|
||||
equation_heat = ObjectsFem.makeEquationHeat(doc, solver_obj)
|
||||
else:
|
||||
@@ -131,22 +131,21 @@ def setup(doc=None, solvertype="elmer"):
|
||||
|
||||
# solver settings
|
||||
equation_flow.IdrsParameter = 3
|
||||
equation_flow.LinearIterations = 250
|
||||
equation_flow.LinearIterativeMethod = "Idrs"
|
||||
equation_flow.LinearPreconditioning = "ILU1"
|
||||
equation_flow.setExpression("LinearTolerance", "1e-6")
|
||||
equation_flow.NonlinearIterations = 30
|
||||
equation_flow.NonlinearNewtonAfterIterations = 30
|
||||
equation_flow.setExpression("NonlinearTolerance", "1e-4")
|
||||
equation_flow.NonlinearIterations = 40
|
||||
equation_flow.NonlinearNewtonAfterIterations = 40
|
||||
equation_flow.RelaxationFactor = 0.1
|
||||
equation_flow.setExpression("NonlinearTolerance", "1e-4")
|
||||
equation_flow.Variable = "Flow Solution[Velocity:2 Pressure:1]"
|
||||
equation_heat.Convection = "Computed"
|
||||
equation_heat.IdrsParameter = 3
|
||||
equation_heat.LinearIterations = 250
|
||||
equation_heat.LinearIterativeMethod = "Idrs"
|
||||
equation_heat.LinearPreconditioning = "ILU1"
|
||||
equation_heat.setExpression("LinearTolerance", "1e-6")
|
||||
equation_heat.NonlinearIterations = 30
|
||||
equation_heat.NonlinearNewtonAfterIterations = 30
|
||||
equation_heat.NonlinearIterations = 40
|
||||
equation_heat.NonlinearNewtonAfterIterations = 40
|
||||
equation_heat.setExpression("NonlinearTolerance", "1e-4")
|
||||
equation_heat.Priority = 5
|
||||
equation_heat.RelaxationFactor = 0.1
|
||||
@@ -186,21 +185,12 @@ def setup(doc=None, solvertype="elmer"):
|
||||
# constraint inlet velocity
|
||||
FlowVelocity_Inlet = ObjectsFem.makeConstraintFlowVelocity(doc, "FlowVelocity_Inlet")
|
||||
FlowVelocity_Inlet.References = [(BooleanFragments, "Edge5")]
|
||||
FlowVelocity_Inlet.NormalDirection = Vector(-1, 0, 0)
|
||||
FlowVelocity_Inlet.VelocityX = 0.020
|
||||
FlowVelocity_Inlet.VelocityXEnabled = True
|
||||
FlowVelocity_Inlet.VelocityYEnabled = True
|
||||
FlowVelocity_Inlet.VelocityZEnabled = True
|
||||
FlowVelocity_Inlet.VelocityXFormula = "Variable Coordinate 2; Real MATC \"10*(tx+50e-3)*(50e-3-tx)\""
|
||||
FlowVelocity_Inlet.VelocityXUnspecified = False
|
||||
FlowVelocity_Inlet.VelocityXHasFormula = True
|
||||
FlowVelocity_Inlet.VelocityYUnspecified = False
|
||||
analysis.addObject(FlowVelocity_Inlet)
|
||||
|
||||
# constraint outlet velocity
|
||||
FlowVelocity_Outlet = ObjectsFem.makeConstraintFlowVelocity(doc, "FlowVelocity_Outlet")
|
||||
FlowVelocity_Outlet.References = [(BooleanFragments, "Edge6")]
|
||||
FlowVelocity_Outlet.NormalDirection = Vector(1, 0, 0)
|
||||
FlowVelocity_Outlet.VelocityYEnabled = True
|
||||
FlowVelocity_Outlet.VelocityZEnabled = True
|
||||
analysis.addObject(FlowVelocity_Outlet)
|
||||
|
||||
# constraint wall velocity
|
||||
FlowVelocity_Wall = ObjectsFem.makeConstraintFlowVelocity(doc, "FlowVelocity_Wall")
|
||||
FlowVelocity_Wall.References = [
|
||||
@@ -208,21 +198,10 @@ def setup(doc=None, solvertype="elmer"):
|
||||
(BooleanFragments, "Edge3"),
|
||||
(BooleanFragments, "Edge4"),
|
||||
(BooleanFragments, "Edge7")]
|
||||
FlowVelocity_Wall.NormalDirection = Vector(0, 0, -1)
|
||||
FlowVelocity_Wall.VelocityXEnabled = True
|
||||
FlowVelocity_Wall.VelocityYEnabled = True
|
||||
FlowVelocity_Wall.VelocityZEnabled = True
|
||||
FlowVelocity_Wall.VelocityXUnspecified = False
|
||||
FlowVelocity_Wall.VelocityYUnspecified = False
|
||||
analysis.addObject(FlowVelocity_Wall)
|
||||
|
||||
# constraint initial velocity
|
||||
FlowVelocity_Initial = ObjectsFem.makeConstraintInitialFlowVelocity(doc, "FlowVelocity_Initial")
|
||||
FlowVelocity_Initial.References = [(BooleanFragments, "Face2")]
|
||||
FlowVelocity_Initial.NormalDirection = Vector(0, -1, 0)
|
||||
FlowVelocity_Initial.VelocityXEnabled = True
|
||||
FlowVelocity_Initial.VelocityYEnabled = True
|
||||
FlowVelocity_Initial.VelocityZEnabled = True
|
||||
analysis.addObject(FlowVelocity_Initial)
|
||||
|
||||
# constraint initial temperature
|
||||
Temperature_Initial = ObjectsFem.makeConstraintInitialTemperature(doc, "Temperature_Initial")
|
||||
Temperature_Initial.initialTemperature = 300.0
|
||||
|
||||
@@ -71,6 +71,7 @@ def run_all():
|
||||
run_example("equation_electrostatics_capacitance_two_balls", run_solver=True)
|
||||
run_example("equation_electrostatics_electricforce_elmer_nongui6", run_solver=True)
|
||||
run_example("equation_flow_elmer_2D", run_solver=True)
|
||||
run_example("equation_flow_initial_elmer_2D", run_solver=True)
|
||||
run_example("equation_flow_turbulent_elmer_2D", run_solver=True)
|
||||
run_example("equation_flux_elmer", run_solver=True)
|
||||
run_example("equation_magnetodynamics_elmer", run_solver=True)
|
||||
@@ -109,6 +110,7 @@ def setup_all():
|
||||
run_example("equation_electrostatics_capacitance_two_balls")
|
||||
run_example("equation_electrostatics_electricforce_elmer_nongui6")
|
||||
run_example("equation_flow_elmer_2D")
|
||||
run_example("equation_flow_initial_elmer_2D")
|
||||
run_example("equation_flow_turbulent_elmer_2D")
|
||||
run_example("equation_flux_elmer")
|
||||
run_example("equation_magnetodynamics_elmer")
|
||||
|
||||
@@ -599,7 +599,7 @@ class FemSelectionObserver:
|
||||
def __init__(self, parseSelectionFunction, print_message=""):
|
||||
self.parseSelectionFunction = parseSelectionFunction
|
||||
FreeCADGui.Selection.addObserver(self)
|
||||
FreeCAD.Console.PrintMessage(print_message + "!\n")
|
||||
#FreeCAD.Console.PrintMessage(print_message + "!\n")
|
||||
|
||||
def addSelection(self, docName, objName, sub, pos):
|
||||
selected_object = FreeCAD.getDocument(docName).getObject(objName) # get the obj objName
|
||||
|
||||
@@ -40,41 +40,83 @@ class ConstraintFlowVelocity(base_fempythonobject.BaseFemPythonObject):
|
||||
def __init__(self, obj):
|
||||
super(ConstraintFlowVelocity, self).__init__(obj)
|
||||
obj.addProperty(
|
||||
"App::PropertyFloat",
|
||||
"App::PropertyVelocity",
|
||||
"VelocityX",
|
||||
"Parameter",
|
||||
"Velocity in x-direction"
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyString",
|
||||
"VelocityXFormula",
|
||||
"Parameter",
|
||||
"Velocity formula in x-direction"
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyBool",
|
||||
"VelocityXEnabled",
|
||||
"VelocityXUnspecified",
|
||||
"Parameter",
|
||||
"Use velocity in x-direction"
|
||||
)
|
||||
obj.VelocityXUnspecified = True
|
||||
obj.addProperty(
|
||||
"App::PropertyFloat",
|
||||
"App::PropertyBool",
|
||||
"VelocityXHasFormula",
|
||||
"Parameter",
|
||||
"Use formula for velocity in x-direction"
|
||||
)
|
||||
|
||||
obj.addProperty(
|
||||
"App::PropertyVelocity",
|
||||
"VelocityY",
|
||||
"Parameter",
|
||||
"Velocity in y-direction"
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyString",
|
||||
"VelocityYFormula",
|
||||
"Parameter",
|
||||
"Velocity formula in y-direction"
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyBool",
|
||||
"VelocityYEnabled",
|
||||
"VelocityYUnspecified",
|
||||
"Parameter",
|
||||
"Use velocity in y-direction"
|
||||
)
|
||||
obj.VelocityYUnspecified = True
|
||||
obj.addProperty(
|
||||
"App::PropertyFloat",
|
||||
"App::PropertyBool",
|
||||
"VelocityYHasFormula",
|
||||
"Parameter",
|
||||
"Use formula for velocity in y-direction"
|
||||
)
|
||||
|
||||
obj.addProperty(
|
||||
"App::PropertyVelocity",
|
||||
"VelocityZ",
|
||||
"Parameter",
|
||||
"Velocity in z-direction"
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyString",
|
||||
"VelocityZFormula",
|
||||
"Parameter",
|
||||
"Velocity formula in z-direction"
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyBool",
|
||||
"VelocityZEnabled",
|
||||
"VelocityZUnspecified",
|
||||
"Parameter",
|
||||
"Use velocity in z-direction"
|
||||
)
|
||||
obj.VelocityZUnspecified = True
|
||||
obj.addProperty(
|
||||
"App::PropertyBool",
|
||||
"VelocityZHasFormula",
|
||||
"Parameter",
|
||||
"Use formula for velocity in z-direction"
|
||||
)
|
||||
|
||||
obj.addProperty(
|
||||
"App::PropertyBool",
|
||||
"NormalToBoundary",
|
||||
|
||||
@@ -40,38 +40,79 @@ class ConstraintInitialFlowVelocity(base_fempythonobject.BaseFemPythonObject):
|
||||
def __init__(self, obj):
|
||||
super(ConstraintInitialFlowVelocity, self).__init__(obj)
|
||||
obj.addProperty(
|
||||
"App::PropertyFloat",
|
||||
"App::PropertyVelocity",
|
||||
"VelocityX",
|
||||
"Parameter",
|
||||
"Velocity in x-direction"
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyString",
|
||||
"VelocityXFormula",
|
||||
"Parameter",
|
||||
"Velocity formula in x-direction"
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyBool",
|
||||
"VelocityXEnabled",
|
||||
"VelocityXUnspecified",
|
||||
"Parameter",
|
||||
"Use velocity in x-direction"
|
||||
)
|
||||
obj.VelocityXUnspecified = True
|
||||
obj.addProperty(
|
||||
"App::PropertyFloat",
|
||||
"App::PropertyBool",
|
||||
"VelocityXHasFormula",
|
||||
"Parameter",
|
||||
"Use formula for velocity in x-direction"
|
||||
)
|
||||
|
||||
obj.addProperty(
|
||||
"App::PropertyVelocity",
|
||||
"VelocityY",
|
||||
"Parameter",
|
||||
"Velocity in y-direction"
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyString",
|
||||
"VelocityYFormula",
|
||||
"Parameter",
|
||||
"Velocity formula in y-direction"
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyBool",
|
||||
"VelocityYEnabled",
|
||||
"VelocityYUnspecified",
|
||||
"Parameter",
|
||||
"Use velocity in y-direction"
|
||||
)
|
||||
obj.VelocityYUnspecified = True
|
||||
obj.addProperty(
|
||||
"App::PropertyFloat",
|
||||
"App::PropertyBool",
|
||||
"VelocityYHasFormula",
|
||||
"Parameter",
|
||||
"Use formula for velocity in y-direction"
|
||||
)
|
||||
|
||||
obj.addProperty(
|
||||
"App::PropertyVelocity",
|
||||
"VelocityZ",
|
||||
"Parameter",
|
||||
"Velocity in z-direction"
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyString",
|
||||
"VelocityZFormula",
|
||||
"Parameter",
|
||||
"Velocity formula in z-direction"
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyBool",
|
||||
"VelocityZEnabled",
|
||||
"VelocityZUnspecified",
|
||||
"Parameter",
|
||||
"Use velocity in z-direction"
|
||||
)
|
||||
obj.VelocityZUnspecified = True
|
||||
obj.addProperty(
|
||||
"App::PropertyBool",
|
||||
"VelocityZHasFormula",
|
||||
"Parameter",
|
||||
"Use formula for velocity in z-direction"
|
||||
)
|
||||
|
||||
@@ -193,14 +193,23 @@ class Flowwriter:
|
||||
def _outputInitialVelocity(self, obj, name):
|
||||
# flow only makes sense for fluid material
|
||||
if self.write.isBodyMaterialFluid(name):
|
||||
if obj.VelocityXEnabled:
|
||||
velocity = self.write.getFromUi(obj.VelocityX, "m/s", "L/T")
|
||||
if not obj.VelocityXUnspecified:
|
||||
if not obj.VelocityXHasFormula:
|
||||
velocity = float(obj.VelocityX.getValueAs("m/s"))
|
||||
else:
|
||||
velocity = obj.VelocityXFormula
|
||||
self.write.initial(name, "Velocity 1", velocity)
|
||||
if obj.VelocityYEnabled:
|
||||
velocity = self.write.getFromUi(obj.VelocityY, "m/s", "L/T")
|
||||
if not obj.VelocityYUnspecified:
|
||||
if not obj.VelocityYHasFormula:
|
||||
velocity = float(obj.VelocityY.getValueAs("m/s"))
|
||||
else:
|
||||
velocity = obj.VelocityYFormula
|
||||
self.write.initial(name, "Velocity 2", velocity)
|
||||
if obj.VelocityZEnabled:
|
||||
velocity = self.write.getFromUi(obj.VelocityZ, "m/s", "L/T")
|
||||
if not obj.VelocityZUnspecified:
|
||||
if not obj.VelocityZHasFormula:
|
||||
velocity = float(obj.VelocityZ.getValueAs("m/s"))
|
||||
else:
|
||||
velocity = obj.VelocityZFormula
|
||||
self.write.initial(name, "Velocity 3", velocity)
|
||||
|
||||
def handleFlowInitialVelocity(self, bodies):
|
||||
@@ -227,14 +236,23 @@ class Flowwriter:
|
||||
for obj in self.write.getMember("Fem::ConstraintFlowVelocity"):
|
||||
if obj.References:
|
||||
for name in obj.References[0][1]:
|
||||
if obj.VelocityXEnabled:
|
||||
velocity = self.write.getFromUi(obj.VelocityX, "m/s", "L/T")
|
||||
if not obj.VelocityXUnspecified:
|
||||
if not obj.VelocityXHasFormula:
|
||||
velocity = float(obj.VelocityX.getValueAs("m/s"))
|
||||
else:
|
||||
velocity = obj.VelocityXFormula
|
||||
self.write.boundary(name, "Velocity 1", velocity)
|
||||
if obj.VelocityYEnabled:
|
||||
velocity = self.write.getFromUi(obj.VelocityY, "m/s", "L/T")
|
||||
if not obj.VelocityYUnspecified:
|
||||
if not obj.VelocityYHasFormula:
|
||||
velocity = float(obj.VelocityY.getValueAs("m/s"))
|
||||
else:
|
||||
velocity = obj.VelocityYFormula
|
||||
self.write.boundary(name, "Velocity 2", velocity)
|
||||
if obj.VelocityZEnabled:
|
||||
velocity = self.write.getFromUi(obj.VelocityZ, "m/s", "L/T")
|
||||
if not obj.VelocityZUnspecified:
|
||||
if not obj.VelocityZHasFormula:
|
||||
velocity = float(obj.VelocityZ.getValueAs("m/s"))
|
||||
else:
|
||||
velocity = obj.VelocityZFormula
|
||||
self.write.boundary(name, "Velocity 3", velocity)
|
||||
if obj.NormalToBoundary:
|
||||
self.write.boundary(name, "Normal-Tangential Velocity", True)
|
||||
|
||||
@@ -76,7 +76,7 @@ class Proxy(linear.Proxy):
|
||||
)
|
||||
)
|
||||
|
||||
obj.NonlinearIterations = (500, 1, int(1e6), 50)
|
||||
obj.NonlinearIterations = (500, 1, int(1e6), 10)
|
||||
obj.NonlinearNewtonAfterIterations = (3, 1, 100, 1)
|
||||
# for small numbers we must set an expression because we don't have a UI,
|
||||
# the user has to view and edit the tolerance via the property editor and
|
||||
|
||||
@@ -77,6 +77,7 @@ _TYPE_INTEGER = "Integer"
|
||||
_TYPE_LOGICAL = "Logical"
|
||||
_TYPE_STRING = "String"
|
||||
_TYPE_FILE = "File"
|
||||
_TYPE_VARIABLE = "Variable"
|
||||
|
||||
WARN = "\"Warn\""
|
||||
IGNORE = "\"Ignore\""
|
||||
@@ -357,9 +358,20 @@ class _Writer(object):
|
||||
self._stream.write(_WHITESPACE)
|
||||
self._stream.write("=")
|
||||
self._stream.write(_WHITESPACE)
|
||||
self._stream.write(attrType)
|
||||
# check if we have a variable string
|
||||
if attrType is _TYPE_STRING:
|
||||
if data.startswith('Variable'):
|
||||
attrType = _TYPE_VARIABLE
|
||||
if attrType is not _TYPE_VARIABLE:
|
||||
self._stream.write(attrType)
|
||||
self._stream.write(_WHITESPACE)
|
||||
self._stream.write(self._preprocess(data, type(data)))
|
||||
output = self._preprocess(data, type(data))
|
||||
# in case of a variable the output must be without the quatoation marks
|
||||
if attrType is _TYPE_VARIABLE:
|
||||
output = output.lstrip('\"')
|
||||
# we cannot use rstrip because there are two subsequent " at the end
|
||||
output = output[:-1]
|
||||
self._stream.write(output)
|
||||
|
||||
def _writeArrAttr(self, key, data):
|
||||
attrType = self._getAttrTypeArr(data)
|
||||
@@ -369,10 +381,21 @@ class _Writer(object):
|
||||
self._stream.write(_WHITESPACE)
|
||||
self._stream.write("=")
|
||||
self._stream.write(_WHITESPACE)
|
||||
self._stream.write(attrType)
|
||||
# check if we have a variable string
|
||||
if attrType is _TYPE_STRING:
|
||||
if data.startswith('Variable'):
|
||||
attrType = _TYPE_VARIABLE
|
||||
if attrType is not _TYPE_VARIABLE:
|
||||
self._stream.write(attrType)
|
||||
for val in data:
|
||||
self._stream.write(_WHITESPACE)
|
||||
self._stream.write(self._preprocess(val, type(val)))
|
||||
output = self._preprocess(val, type(val))
|
||||
# in case of a variable the output must be without the quatoation marks
|
||||
if attrType is _TYPE_VARIABLE:
|
||||
output = output.lstrip('\"')
|
||||
# we cannot use rstrip because there are two subsequent " at the end
|
||||
output = output[:-1]
|
||||
self._stream.write(output)
|
||||
|
||||
def _writeFileAttr(self, key, data):
|
||||
self._stream.write(_INDENT)
|
||||
|
||||
@@ -29,9 +29,10 @@ __url__ = "https://www.freecadweb.org"
|
||||
# \ingroup FEM
|
||||
# \brief task panel for constraint flow velocity object
|
||||
|
||||
from PySide import QtCore
|
||||
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
from FreeCAD import Units
|
||||
|
||||
from femguiutils import selection_widgets
|
||||
from femtools import femutils
|
||||
@@ -46,8 +47,7 @@ class _TaskPanel(object):
|
||||
self._paramWidget = FreeCADGui.PySideUic.loadUi(
|
||||
FreeCAD.getHomePath() + "Mod/Fem/Resources/ui/FlowVelocity.ui"
|
||||
)
|
||||
self._initParamWidget()
|
||||
|
||||
|
||||
# geometry selection widget
|
||||
# start with Solid in list!
|
||||
self._selectionWidget = selection_widgets.GeometryElementsSelection(
|
||||
@@ -70,6 +70,96 @@ class _TaskPanel(object):
|
||||
self._partVisible = None
|
||||
self._meshVisible = None
|
||||
|
||||
# connect unspecified option
|
||||
QtCore.QObject.connect(
|
||||
self._paramWidget.velocityXBox,
|
||||
QtCore.SIGNAL("toggled(bool)"),
|
||||
self._velocityXEnable
|
||||
)
|
||||
QtCore.QObject.connect(
|
||||
self._paramWidget.velocityYBox,
|
||||
QtCore.SIGNAL("toggled(bool)"),
|
||||
self._velocityYEnable
|
||||
)
|
||||
QtCore.QObject.connect(
|
||||
self._paramWidget.velocityZBox,
|
||||
QtCore.SIGNAL("toggled(bool)"),
|
||||
self._velocityZEnable
|
||||
)
|
||||
|
||||
# connect formula option
|
||||
QtCore.QObject.connect(
|
||||
self._paramWidget.formulaXCB,
|
||||
QtCore.SIGNAL("toggled(bool)"),
|
||||
self._formulaXEnable
|
||||
)
|
||||
QtCore.QObject.connect(
|
||||
self._paramWidget.formulaYCB,
|
||||
QtCore.SIGNAL("toggled(bool)"),
|
||||
self._formulaYEnable
|
||||
)
|
||||
QtCore.QObject.connect(
|
||||
self._paramWidget.formulaZCB,
|
||||
QtCore.SIGNAL("toggled(bool)"),
|
||||
self._formulaZEnable
|
||||
)
|
||||
|
||||
self._initParamWidget()
|
||||
|
||||
def _velocityXEnable(self, toggled):
|
||||
if toggled:
|
||||
self._paramWidget.formulaX.setDisabled(toggled)
|
||||
self._paramWidget.velocityX.setDisabled(toggled)
|
||||
else:
|
||||
if self._paramWidget.formulaXCB.isChecked():
|
||||
self._paramWidget.formulaX.setDisabled(toggled)
|
||||
else:
|
||||
self._paramWidget.velocityX.setDisabled(toggled)
|
||||
|
||||
def _velocityYEnable(self, toggled):
|
||||
if toggled:
|
||||
self._paramWidget.formulaY.setDisabled(toggled)
|
||||
self._paramWidget.velocityY.setDisabled(toggled)
|
||||
else:
|
||||
if self._paramWidget.formulaYCB.isChecked():
|
||||
self._paramWidget.formulaY.setDisabled(toggled)
|
||||
else:
|
||||
self._paramWidget.velocityY.setDisabled(toggled)
|
||||
|
||||
def _velocityZEnable(self, toggled):
|
||||
if toggled:
|
||||
self._paramWidget.formulaZ.setDisabled(toggled)
|
||||
self._paramWidget.velocityZ.setDisabled(toggled)
|
||||
else:
|
||||
if self._paramWidget.formulaZCB.isChecked():
|
||||
self._paramWidget.formulaZ.setDisabled(toggled)
|
||||
else:
|
||||
self._paramWidget.velocityZ.setDisabled(toggled)
|
||||
|
||||
def _formulaXEnable(self, toggled):
|
||||
FreeCAD.Console.PrintMessage("_formulaXEnable\n")
|
||||
if self._paramWidget.velocityXBox.isChecked():
|
||||
FreeCAD.Console.PrintMessage("velocityXBox isChecked\n")
|
||||
return
|
||||
else:
|
||||
FreeCAD.Console.PrintMessage("velocityXBox not checked\n")
|
||||
self._paramWidget.formulaX.setEnabled(toggled)
|
||||
self._paramWidget.velocityX.setDisabled(toggled)
|
||||
|
||||
def _formulaYEnable(self, toggled):
|
||||
if self._paramWidget.velocityYBox.isChecked():
|
||||
return
|
||||
else:
|
||||
self._paramWidget.formulaY.setEnabled(toggled)
|
||||
self._paramWidget.velocityY.setDisabled(toggled)
|
||||
|
||||
def _formulaZEnable(self, toggled):
|
||||
if self._paramWidget.velocitZXBox.isChecked():
|
||||
return
|
||||
else:
|
||||
self._paramWidget.formulaZ.setEnabled(toggled)
|
||||
self._paramWidget.velocityZ.setDisabled(toggled)
|
||||
|
||||
def open(self):
|
||||
if self._mesh is not None and self._part is not None:
|
||||
self._meshVisible = self._mesh.ViewObject.isVisible()
|
||||
@@ -104,36 +194,80 @@ class _TaskPanel(object):
|
||||
|
||||
def _initParamWidget(self):
|
||||
unit = "m/s"
|
||||
self._paramWidget.velocityXTxt.setText(
|
||||
str(self._obj.VelocityX) + unit)
|
||||
self._paramWidget.velocityYTxt.setText(
|
||||
str(self._obj.VelocityY) + unit)
|
||||
self._paramWidget.velocityZTxt.setText(
|
||||
str(self._obj.VelocityZ) + unit)
|
||||
self._paramWidget.velocityX.setProperty('unit', unit)
|
||||
self._paramWidget.velocityY.setProperty('unit', unit)
|
||||
self._paramWidget.velocityZ.setProperty('unit', unit)
|
||||
|
||||
self._paramWidget.velocityX.setProperty(
|
||||
'value', self._obj.VelocityX)
|
||||
FreeCADGui.ExpressionBinding(
|
||||
self._paramWidget.velocityX).bind(self._obj, "VelocityX")
|
||||
self._paramWidget.velocityXBox.setChecked(
|
||||
not self._obj.VelocityXEnabled)
|
||||
self._obj.VelocityXUnspecified)
|
||||
self._paramWidget.formulaX.setText(self._obj.VelocityXFormula)
|
||||
self._paramWidget.formulaXCB.setChecked(
|
||||
self._obj.VelocityXHasFormula)
|
||||
|
||||
self._paramWidget.velocityY.setProperty(
|
||||
'value', self._obj.VelocityY)
|
||||
FreeCADGui.ExpressionBinding(
|
||||
self._paramWidget.velocityY).bind(self._obj, "VelocityY")
|
||||
self._paramWidget.velocityYBox.setChecked(
|
||||
not self._obj.VelocityYEnabled)
|
||||
self._obj.VelocityYUnspecified)
|
||||
self._paramWidget.formulaY.setText(self._obj.VelocityYFormula)
|
||||
self._paramWidget.formulaYCB.setChecked(
|
||||
self._obj.VelocityYHasFormula)
|
||||
|
||||
self._paramWidget.velocityZ.setProperty(
|
||||
'value', self._obj.VelocityZ)
|
||||
FreeCADGui.ExpressionBinding(
|
||||
self._paramWidget.velocityZ).bind(self._obj, "VelocityZ")
|
||||
self._paramWidget.velocityZBox.setChecked(
|
||||
not self._obj.VelocityZEnabled)
|
||||
self._obj.VelocityZUnspecified)
|
||||
self._paramWidget.formulaZ.setText(self._obj.VelocityZFormula)
|
||||
self._paramWidget.formulaZCB.setChecked(
|
||||
self._obj.VelocityZHasFormula)
|
||||
|
||||
self._paramWidget.normalBox.setChecked(
|
||||
self._obj.NormalToBoundary)
|
||||
|
||||
def _applyVelocityChanges(self, enabledBox, velocityQSB):
|
||||
enabled = enabledBox.isChecked()
|
||||
velocity = None
|
||||
try:
|
||||
velocity = velocityQSB.property('value')
|
||||
except ValueError:
|
||||
FreeCAD.Console.PrintMessage(
|
||||
"Wrong input. Not recognised input: '{}' "
|
||||
"Velocity has not been set.\n".format(velocityQSB.text())
|
||||
)
|
||||
velocity = '0.0 m/s'
|
||||
return enabled, velocity
|
||||
|
||||
def _applyWidgetChanges(self):
|
||||
unit = "m/s"
|
||||
self._obj.VelocityXEnabled = \
|
||||
not self._paramWidget.velocityXBox.isChecked()
|
||||
if self._obj.VelocityXEnabled:
|
||||
quantity = Units.Quantity(self._paramWidget.velocityXTxt.text())
|
||||
self._obj.VelocityX = quantity.getValueAs(unit).Value
|
||||
self._obj.VelocityYEnabled = \
|
||||
not self._paramWidget.velocityYBox.isChecked()
|
||||
if self._obj.VelocityYEnabled:
|
||||
quantity = Units.Quantity(self._paramWidget.velocityYTxt.text())
|
||||
self._obj.VelocityY = quantity.getValueAs(unit).Value
|
||||
self._obj.VelocityZEnabled = \
|
||||
not self._paramWidget.velocityZBox.isChecked()
|
||||
if self._obj.VelocityZEnabled:
|
||||
quantity = Units.Quantity(self._paramWidget.velocityZTxt.text())
|
||||
self._obj.VelocityZ = quantity.getValueAs(unit).Value
|
||||
# apply the velocities and their enabled state
|
||||
self._obj.VelocityXUnspecified, self._obj.VelocityX = \
|
||||
self._applyVelocityChanges(
|
||||
self._paramWidget.velocityXBox,
|
||||
self._paramWidget.velocityX
|
||||
)
|
||||
self._obj.VelocityXHasFormula = self._paramWidget.formulaXCB.isChecked()
|
||||
self._obj.VelocityXFormula = self._paramWidget.formulaX.text()
|
||||
|
||||
self._obj.VelocityYUnspecified, self._obj.VelocityY = \
|
||||
self._applyVelocityChanges(
|
||||
self._paramWidget.velocityYBox,
|
||||
self._paramWidget.velocityY
|
||||
)
|
||||
self._obj.VelocityYHasFormula = self._paramWidget.formulaYCB.isChecked()
|
||||
self._obj.VelocityYFormula = self._paramWidget.formulaY.text()
|
||||
|
||||
self._obj.VelocityZUnspecified, self._obj.VelocityZ = \
|
||||
self._applyVelocityChanges(
|
||||
self._paramWidget.velocityZBox,
|
||||
self._paramWidget.velocityZ
|
||||
)
|
||||
self._obj.VelocityZHasFormula = self._paramWidget.formulaZCB.isChecked()
|
||||
self._obj.VelocityZFormula = self._paramWidget.formulaZ.text()
|
||||
|
||||
self._obj.NormalToBoundary = self._paramWidget.normalBox.isChecked()
|
||||
|
||||
@@ -30,6 +30,8 @@ __url__ = "https://www.freecadweb.org"
|
||||
# \ingroup FEM
|
||||
# \brief task panel for constraint initial flow velocity object
|
||||
|
||||
from PySide import QtCore
|
||||
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
from FreeCAD import Units
|
||||
@@ -46,7 +48,6 @@ class _TaskPanel(object):
|
||||
|
||||
self._paramWidget = FreeCADGui.PySideUic.loadUi(
|
||||
FreeCAD.getHomePath() + "Mod/Fem/Resources/ui/InitialFlowVelocity.ui")
|
||||
self._initParamWidget()
|
||||
|
||||
# geometry selection widget
|
||||
# start with Solid in list!
|
||||
@@ -70,6 +71,96 @@ class _TaskPanel(object):
|
||||
self._partVisible = None
|
||||
self._meshVisible = None
|
||||
|
||||
# connect unspecified option
|
||||
QtCore.QObject.connect(
|
||||
self._paramWidget.velocityXBox,
|
||||
QtCore.SIGNAL("toggled(bool)"),
|
||||
self._velocityXEnable
|
||||
)
|
||||
QtCore.QObject.connect(
|
||||
self._paramWidget.velocityYBox,
|
||||
QtCore.SIGNAL("toggled(bool)"),
|
||||
self._velocityYEnable
|
||||
)
|
||||
QtCore.QObject.connect(
|
||||
self._paramWidget.velocityZBox,
|
||||
QtCore.SIGNAL("toggled(bool)"),
|
||||
self._velocityZEnable
|
||||
)
|
||||
|
||||
# connect formula option
|
||||
QtCore.QObject.connect(
|
||||
self._paramWidget.formulaXCB,
|
||||
QtCore.SIGNAL("toggled(bool)"),
|
||||
self._formulaXEnable
|
||||
)
|
||||
QtCore.QObject.connect(
|
||||
self._paramWidget.formulaYCB,
|
||||
QtCore.SIGNAL("toggled(bool)"),
|
||||
self._formulaYEnable
|
||||
)
|
||||
QtCore.QObject.connect(
|
||||
self._paramWidget.formulaZCB,
|
||||
QtCore.SIGNAL("toggled(bool)"),
|
||||
self._formulaZEnable
|
||||
)
|
||||
|
||||
self._initParamWidget()
|
||||
|
||||
def _velocityXEnable(self, toggled):
|
||||
if toggled:
|
||||
self._paramWidget.formulaX.setDisabled(toggled)
|
||||
self._paramWidget.velocityX.setDisabled(toggled)
|
||||
else:
|
||||
if self._paramWidget.formulaXCB.isChecked():
|
||||
self._paramWidget.formulaX.setDisabled(toggled)
|
||||
else:
|
||||
self._paramWidget.velocityX.setDisabled(toggled)
|
||||
|
||||
def _velocityYEnable(self, toggled):
|
||||
if toggled:
|
||||
self._paramWidget.formulaY.setDisabled(toggled)
|
||||
self._paramWidget.velocityY.setDisabled(toggled)
|
||||
else:
|
||||
if self._paramWidget.formulaYCB.isChecked():
|
||||
self._paramWidget.formulaY.setDisabled(toggled)
|
||||
else:
|
||||
self._paramWidget.velocityY.setDisabled(toggled)
|
||||
|
||||
def _velocityZEnable(self, toggled):
|
||||
if toggled:
|
||||
self._paramWidget.formulaZ.setDisabled(toggled)
|
||||
self._paramWidget.velocityZ.setDisabled(toggled)
|
||||
else:
|
||||
if self._paramWidget.formulaZCB.isChecked():
|
||||
self._paramWidget.formulaZ.setDisabled(toggled)
|
||||
else:
|
||||
self._paramWidget.velocityZ.setDisabled(toggled)
|
||||
|
||||
def _formulaXEnable(self, toggled):
|
||||
FreeCAD.Console.PrintMessage("_formulaXEnable\n")
|
||||
if self._paramWidget.velocityXBox.isChecked():
|
||||
FreeCAD.Console.PrintMessage("velocityXBox isChecked\n")
|
||||
return
|
||||
else:
|
||||
FreeCAD.Console.PrintMessage("velocityXBox not checked\n")
|
||||
self._paramWidget.formulaX.setEnabled(toggled)
|
||||
self._paramWidget.velocityX.setDisabled(toggled)
|
||||
|
||||
def _formulaYEnable(self, toggled):
|
||||
if self._paramWidget.velocityYBox.isChecked():
|
||||
return
|
||||
else:
|
||||
self._paramWidget.formulaY.setEnabled(toggled)
|
||||
self._paramWidget.velocityY.setDisabled(toggled)
|
||||
|
||||
def _formulaZEnable(self, toggled):
|
||||
if self._paramWidget.velocitZXBox.isChecked():
|
||||
return
|
||||
else:
|
||||
self._paramWidget.formulaZ.setEnabled(toggled)
|
||||
self._paramWidget.velocityZ.setDisabled(toggled)
|
||||
|
||||
def open(self):
|
||||
if self._mesh is not None and self._part is not None:
|
||||
self._meshVisible = self._mesh.ViewObject.isVisible()
|
||||
@@ -104,33 +195,75 @@ class _TaskPanel(object):
|
||||
|
||||
def _initParamWidget(self):
|
||||
unit = "m/s"
|
||||
self._paramWidget.velocityXTxt.setText(
|
||||
str(self._obj.VelocityX) + unit)
|
||||
self._paramWidget.velocityYTxt.setText(
|
||||
str(self._obj.VelocityY) + unit)
|
||||
self._paramWidget.velocityZTxt.setText(
|
||||
str(self._obj.VelocityZ) + unit)
|
||||
self._paramWidget.velocityX.setProperty('unit', unit)
|
||||
self._paramWidget.velocityY.setProperty('unit', unit)
|
||||
self._paramWidget.velocityZ.setProperty('unit', unit)
|
||||
|
||||
self._paramWidget.velocityX.setProperty(
|
||||
'value', self._obj.VelocityX)
|
||||
FreeCADGui.ExpressionBinding(
|
||||
self._paramWidget.velocityX).bind(self._obj, "VelocityX")
|
||||
self._paramWidget.velocityXBox.setChecked(
|
||||
not self._obj.VelocityXEnabled)
|
||||
self._obj.VelocityXUnspecified)
|
||||
self._paramWidget.formulaX.setText(self._obj.VelocityXFormula)
|
||||
self._paramWidget.formulaXCB.setChecked(
|
||||
self._obj.VelocityXHasFormula)
|
||||
|
||||
self._paramWidget.velocityY.setProperty(
|
||||
'value', self._obj.VelocityY)
|
||||
FreeCADGui.ExpressionBinding(
|
||||
self._paramWidget.velocityY).bind(self._obj, "VelocityY")
|
||||
self._paramWidget.velocityYBox.setChecked(
|
||||
not self._obj.VelocityYEnabled)
|
||||
self._obj.VelocityYUnspecified)
|
||||
self._paramWidget.formulaY.setText(self._obj.VelocityYFormula)
|
||||
self._paramWidget.formulaYCB.setChecked(
|
||||
self._obj.VelocityYHasFormula)
|
||||
|
||||
self._paramWidget.velocityZ.setProperty(
|
||||
'value', self._obj.VelocityZ)
|
||||
FreeCADGui.ExpressionBinding(
|
||||
self._paramWidget.velocityZ).bind(self._obj, "VelocityZ")
|
||||
self._paramWidget.velocityZBox.setChecked(
|
||||
not self._obj.VelocityZEnabled)
|
||||
self._obj.VelocityZUnspecified)
|
||||
self._paramWidget.formulaZ.setText(self._obj.VelocityZFormula)
|
||||
self._paramWidget.formulaZCB.setChecked(
|
||||
self._obj.VelocityZHasFormula)
|
||||
|
||||
def _applyVelocityChanges(self, enabledBox, velocityQSB):
|
||||
enabled = enabledBox.isChecked()
|
||||
velocity = None
|
||||
try:
|
||||
velocity = velocityQSB.property('value')
|
||||
except ValueError:
|
||||
FreeCAD.Console.PrintMessage(
|
||||
"Wrong input. Not recognised input: '{}' "
|
||||
"Velocity has not been set.\n".format(velocityQSB.text())
|
||||
)
|
||||
velocity = '0.0 m/s'
|
||||
return enabled, velocity
|
||||
|
||||
def _applyWidgetChanges(self):
|
||||
unit = "m/s"
|
||||
self._obj.VelocityXEnabled = \
|
||||
not self._paramWidget.velocityXBox.isChecked()
|
||||
if self._obj.VelocityXEnabled:
|
||||
quantity = Units.Quantity(self._paramWidget.velocityXTxt.text())
|
||||
self._obj.VelocityX = quantity.getValueAs(unit).Value
|
||||
self._obj.VelocityYEnabled = \
|
||||
not self._paramWidget.velocityYBox.isChecked()
|
||||
if self._obj.VelocityYEnabled:
|
||||
quantity = Units.Quantity(self._paramWidget.velocityYTxt.text())
|
||||
self._obj.VelocityY = quantity.getValueAs(unit).Value
|
||||
self._obj.VelocityZEnabled = \
|
||||
not self._paramWidget.velocityZBox.isChecked()
|
||||
if self._obj.VelocityZEnabled:
|
||||
quantity = Units.Quantity(self._paramWidget.velocityZTxt.text())
|
||||
self._obj.VelocityZ = quantity.getValueAs(unit).Value
|
||||
# apply the velocities and their enabled state
|
||||
self._obj.VelocityXUnspecified, self._obj.VelocityX = \
|
||||
self._applyVelocityChanges(
|
||||
self._paramWidget.velocityXBox,
|
||||
self._paramWidget.velocityX
|
||||
)
|
||||
self._obj.VelocityXHasFormula = self._paramWidget.formulaXCB.isChecked()
|
||||
self._obj.VelocityXFormula = self._paramWidget.formulaX.text()
|
||||
|
||||
self._obj.VelocityYUnspecified, self._obj.VelocityY = \
|
||||
self._applyVelocityChanges(
|
||||
self._paramWidget.velocityYBox,
|
||||
self._paramWidget.velocityY
|
||||
)
|
||||
self._obj.VelocityYHasFormula = self._paramWidget.formulaYCB.isChecked()
|
||||
self._obj.VelocityYFormula = self._paramWidget.formulaY.text()
|
||||
|
||||
self._obj.VelocityZUnspecified, self._obj.VelocityZ = \
|
||||
self._applyVelocityChanges(
|
||||
self._paramWidget.velocityZBox,
|
||||
self._paramWidget.velocityZ
|
||||
)
|
||||
self._obj.VelocityZHasFormula = self._paramWidget.formulaZCB.isChecked()
|
||||
self._obj.VelocityZFormula = self._paramWidget.formulaZ.text()
|
||||
|
||||
@@ -295,7 +295,7 @@ class _TaskPanel:
|
||||
CCX_mesh = self.fea.analysis.Document.getObject("ResultMesh")
|
||||
if CCX_mesh is not None:
|
||||
CCX_mesh.ViewObject.Visibility = self.CCX_mesh_visibility
|
||||
|
||||
|
||||
def choose_working_dir(self):
|
||||
wd = QtGui.QFileDialog.getExistingDirectory(None, "Choose CalculiX working directory",
|
||||
self.fea.working_dir)
|
||||
|
||||
@@ -224,14 +224,6 @@ int ConstraintPy::PyInit(PyObject* args, PyObject* /*kwd*/)
|
||||
if (PyNumber_Check(index_or_value)) { // can be float or int
|
||||
SecondIndex = any_index;
|
||||
Value = PyFloat_AsDouble(index_or_value);
|
||||
//if (strcmp("Distance",ConstraintType) == 0) {
|
||||
// this->getConstraintPtr()->Type = Distance;
|
||||
// this->getConstraintPtr()->First = FirstIndex;
|
||||
// this->getConstraintPtr()->Second = SecondIndex;
|
||||
// this->getConstraintPtr()->Value = Value;
|
||||
// return 0;
|
||||
//}
|
||||
//else
|
||||
if (strcmp("Angle",ConstraintType) == 0) {
|
||||
if (PyObject_TypeCheck(index_or_value, &(Base::QuantityPy::Type))) {
|
||||
Base::Quantity q = *(static_cast<Base::QuantityPy*>(index_or_value)->getQuantityPtr());
|
||||
@@ -244,6 +236,13 @@ int ConstraintPy::PyInit(PyObject* args, PyObject* /*kwd*/)
|
||||
this->getConstraintPtr()->setValue(Value);
|
||||
return 0;
|
||||
}
|
||||
else if (strcmp("Distance",ConstraintType) == 0) {
|
||||
this->getConstraintPtr()->Type = Distance;
|
||||
this->getConstraintPtr()->First = FirstIndex;
|
||||
this->getConstraintPtr()->Second = SecondIndex;
|
||||
this->getConstraintPtr()->setValue(Value);
|
||||
return 0;
|
||||
}
|
||||
else if (strcmp("DistanceX",ConstraintType) == 0) {
|
||||
FirstPos = SecondIndex;
|
||||
SecondIndex = -1;
|
||||
@@ -481,7 +480,7 @@ std::string ConstraintPy::representation() const
|
||||
case Coincident : result << "'Coincident'>";break;
|
||||
case Horizontal : result << "'Horizontal' (" << getConstraintPtr()->First << ")>";break;
|
||||
case Vertical : result << "'Vertical' (" << getConstraintPtr()->First << ")>";break;
|
||||
case Block : result << "'Block' (" << getConstraintPtr()->First << ")>";break;
|
||||
case Block : result << "'Block' (" << getConstraintPtr()->First << ")>";break;
|
||||
case Radius : result << "'Radius'>";break;
|
||||
case Diameter : result << "'Diameter'>";break;
|
||||
case Weight : result << "'Weight'>";break;
|
||||
|
||||
@@ -296,6 +296,10 @@ std::string PythonConverter::process(const Sketcher::Constraint * constraint)
|
||||
return boost::str(boost::format("Sketcher.Constraint('Distance', %i, %f)") %
|
||||
constr->First % constr->getValue());
|
||||
}
|
||||
else if(constr->FirstPos == Sketcher::PointPos::none){
|
||||
return boost::str(boost::format("Sketcher.Constraint('Distance', %i, %i, %f)") %
|
||||
constr->First % constr->Second % constr->getValue());
|
||||
}
|
||||
else if(constr->SecondPos == Sketcher::PointPos::none){
|
||||
return boost::str(boost::format("Sketcher.Constraint('Distance', %i, %i, %i, %f)") %
|
||||
constr->First % static_cast<int>(constr->FirstPos) % constr->Second % constr->getValue());
|
||||
|
||||
@@ -1797,6 +1797,20 @@ int Sketch::addConstraint(const Constraint *constraint)
|
||||
constraint->Second,constraint->SecondPos,
|
||||
c.value,c.driving);
|
||||
}
|
||||
else if (constraint->FirstPos == PointPos::none &&
|
||||
constraint->SecondPos == PointPos::none &&
|
||||
constraint->Second != GeoEnum::GeoUndef &&
|
||||
constraint->Third == GeoEnum::GeoUndef) { // circle to circle, circle to arc, etc.
|
||||
|
||||
c.value = new double(constraint->getValue());
|
||||
if(c.driving)
|
||||
FixParameters.push_back(c.value);
|
||||
else {
|
||||
Parameters.push_back(c.value);
|
||||
DrivenParameters.push_back(c.value);
|
||||
}
|
||||
rtn = addDistanceConstraint(constraint->First, constraint->Second,c.value,c.driving);
|
||||
}
|
||||
else if (constraint->Second != GeoEnum::GeoUndef) {
|
||||
if (constraint->FirstPos != PointPos::none) { // point to line distance
|
||||
c.value = new double(constraint->getValue());
|
||||
@@ -1806,8 +1820,7 @@ int Sketch::addConstraint(const Constraint *constraint)
|
||||
Parameters.push_back(c.value);
|
||||
DrivenParameters.push_back(c.value);
|
||||
}
|
||||
rtn = addDistanceConstraint(constraint->First,constraint->FirstPos,
|
||||
constraint->Second,c.value,c.driving);
|
||||
rtn = addDistanceConstraint(constraint->First,constraint->FirstPos,constraint->Second,c.value,c.driving);
|
||||
}
|
||||
}
|
||||
else {// line length
|
||||
@@ -2740,6 +2753,19 @@ int Sketch::addDistanceConstraint(int geoId1, PointPos pos1, int geoId2, PointPo
|
||||
return -1;
|
||||
}
|
||||
|
||||
// circle-circle offset distance constraint
|
||||
int Sketch::addDistanceConstraint(int geoId1, int geoId2, double * value, bool driving)
|
||||
{
|
||||
if ((Geoms[geoId1].type == Circle) && (Geoms[geoId2].type == Circle)) {
|
||||
GCS::Circle &c1 = Circles[Geoms[geoId1].index];
|
||||
GCS::Circle &c2 = Circles[Geoms[geoId2].index];
|
||||
int tag = ++ConstraintsCounter;
|
||||
GCSsys.addConstraintC2CDistance(c1, c2, value, tag, driving);
|
||||
return ConstraintsCounter;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int Sketch::addRadiusConstraint(int geoId, double * value, bool driving)
|
||||
{
|
||||
geoId = checkGeoId(geoId);
|
||||
|
||||
@@ -276,6 +276,15 @@ public:
|
||||
* Parameters array, as the case may be.
|
||||
*/
|
||||
int addDistanceConstraint(int geoId1, PointPos pos1, int geoId2, PointPos pos2, double * value, bool driving = true);
|
||||
/**
|
||||
* add a length or distance constraint
|
||||
*
|
||||
* double * value is a pointer to double allocated in the heap, containing the
|
||||
* constraint value and already inserted into either the FixParameters or
|
||||
* Parameters array, as the case may be.
|
||||
*/
|
||||
int addDistanceConstraint(int geoId1, int geoId2, double * value, bool driving = true);
|
||||
|
||||
/// add a parallel constraint between two lines
|
||||
int addParallelConstraint(int geoId1, int geoId2);
|
||||
/// add a perpendicular constraint between two lines
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -72,7 +72,8 @@ namespace GCS
|
||||
CenterOfGravity = 26,
|
||||
WeightedLinearCombination = 27,
|
||||
SlopeAtBSplineKnot = 28,
|
||||
PointOnBSpline = 29
|
||||
PointOnBSpline = 29,
|
||||
C2CDistance = 30
|
||||
};
|
||||
|
||||
enum InternalAlignmentType {
|
||||
@@ -745,6 +746,22 @@ namespace GCS
|
||||
double grad(double *) override;
|
||||
};
|
||||
|
||||
class ConstraintC2CDistance : public Constraint
|
||||
{
|
||||
private:
|
||||
Circle c1;
|
||||
Circle c2;
|
||||
double *d;
|
||||
inline double* distance() { return pvec[0]; }
|
||||
void ReconstructGeomPointers(); //writes pointers in pvec to the parameters of c1, c2
|
||||
void errorgrad(double* err, double* grad, double *param); //error and gradient combined. Values are returned through pointers.
|
||||
public:
|
||||
ConstraintC2CDistance(Circle &c1, Circle &c2, double *d);
|
||||
ConstraintType getTypeId() override;
|
||||
void rescale(double coef=1.) override;
|
||||
double error() override;
|
||||
double grad(double *) override;
|
||||
};
|
||||
|
||||
} //namespace GCS
|
||||
|
||||
|
||||
@@ -817,6 +817,14 @@ int System::addConstraintTangentAtBSplineKnot(BSpline &b, Line &l, unsigned int
|
||||
return addConstraint(constr);
|
||||
}
|
||||
|
||||
int System::addConstraintC2CDistance(Circle &c1, Circle &c2, double *dist, int tagId, bool driving)
|
||||
{
|
||||
Constraint *constr = new ConstraintC2CDistance(c1, c2, dist);
|
||||
constr->setTag(tagId);
|
||||
constr->setDriving(driving);
|
||||
return addConstraint(constr);
|
||||
}
|
||||
|
||||
// derived constraints
|
||||
|
||||
int System::addConstraintP2PCoincident(Point &p1, Point &p2, int tagId, bool driving)
|
||||
|
||||
@@ -315,6 +315,8 @@ namespace GCS
|
||||
bool flipn1, bool flipn2,
|
||||
int tagId, bool driving = true);
|
||||
|
||||
int addConstraintC2CDistance(Circle &c1, Circle &c2, double *dist, int tagId, bool driving = true);
|
||||
|
||||
// internal alignment constraints
|
||||
int addConstraintInternalAlignmentPoint2Ellipse(Ellipse &e, Point &p1, InternalAlignmentType alignmentType, int tagId=0, bool driving = true);
|
||||
int addConstraintInternalAlignmentEllipseMajorDiameter(Ellipse &e, Point &p1, Point &p2, int tagId=0, bool driving = true);
|
||||
|
||||
@@ -144,6 +144,8 @@ SET(SketcherGui_SRCS
|
||||
SketchRectangularArrayDialog.cpp
|
||||
SketcherRegularPolygonDialog.h
|
||||
SketcherRegularPolygonDialog.cpp
|
||||
SnapManager.cpp
|
||||
SnapManager.h
|
||||
TaskDlgEditSketch.cpp
|
||||
TaskDlgEditSketch.h
|
||||
ViewProviderPython.cpp
|
||||
|
||||
@@ -995,8 +995,6 @@ public:
|
||||
updateCheckBox(checkbox, propvalue);
|
||||
};
|
||||
|
||||
updateCheckBox(gridSnap, sketchView->getSnapMode() == SnapMode::SnapToGrid);
|
||||
|
||||
updateCheckBoxFromProperty(gridAutoSpacing, sketchView->GridAuto);
|
||||
|
||||
gridSizeBox->setValue(sketchView->GridSize.getValue());
|
||||
@@ -1005,10 +1003,6 @@ public:
|
||||
|
||||
void languageChange()
|
||||
{
|
||||
gridSnap->setText(tr("Grid Snap"));
|
||||
gridSnap->setToolTip(tr("New points will snap to the nearest grid line.\nPoints must be set closer than a fifth of the grid spacing to a grid line to snap."));
|
||||
gridSnap->setStatusTip(gridSnap->toolTip());
|
||||
|
||||
gridAutoSpacing->setText(tr("Grid Auto Spacing"));
|
||||
gridAutoSpacing->setToolTip(tr("Resize grid automatically depending on zoom."));
|
||||
gridAutoSpacing->setStatusTip(gridAutoSpacing->toolTip());
|
||||
@@ -1020,8 +1014,6 @@ public:
|
||||
protected:
|
||||
QWidget* createWidget(QWidget* parent) override
|
||||
{
|
||||
gridSnap = new QCheckBox();
|
||||
|
||||
gridAutoSpacing = new QCheckBox();
|
||||
|
||||
sizeLabel = new QLabel();
|
||||
@@ -1034,27 +1026,12 @@ protected:
|
||||
|
||||
QWidget* gridSizeW = new QWidget(parent);
|
||||
auto* layout = new QGridLayout(gridSizeW);
|
||||
layout->addWidget(gridSnap, 0, 0);
|
||||
layout->addWidget(gridAutoSpacing, 1, 0);
|
||||
layout->addWidget(sizeLabel, 2, 0);
|
||||
layout->addWidget(gridSizeBox, 2, 1);
|
||||
layout->addWidget(gridAutoSpacing, 0, 0, 1, 2);
|
||||
layout->addWidget(sizeLabel, 1, 0);
|
||||
layout->addWidget(gridSizeBox, 1, 1);
|
||||
|
||||
languageChange();
|
||||
|
||||
QObject::connect(gridSnap, &QCheckBox::stateChanged, [this](int state) {
|
||||
auto* sketchView = getView();
|
||||
|
||||
if(sketchView) {
|
||||
if(state == Qt::Checked) {
|
||||
sketchView->setSnapMode(SnapMode::SnapToGrid);
|
||||
}
|
||||
else {
|
||||
sketchView->setSnapMode(SnapMode::None);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
QObject::connect(gridAutoSpacing, &QCheckBox::stateChanged, [this](int state) {
|
||||
auto* sketchView = getView();
|
||||
|
||||
@@ -1086,7 +1063,6 @@ private:
|
||||
}
|
||||
|
||||
private:
|
||||
QCheckBox * gridSnap;
|
||||
QCheckBox * gridAutoSpacing;
|
||||
QLabel * sizeLabel;
|
||||
Gui::QuantitySpinBox * gridSizeBox;
|
||||
@@ -1097,13 +1073,13 @@ class CmdSketcherGrid : public Gui::Command
|
||||
public:
|
||||
CmdSketcherGrid();
|
||||
virtual ~CmdSketcherGrid(){}
|
||||
virtual const char* className() const
|
||||
virtual const char* className() const override
|
||||
{ return "CmdSketcherGrid"; }
|
||||
virtual void languageChange();
|
||||
virtual void languageChange() override;
|
||||
protected:
|
||||
virtual void activated(int iMsg);
|
||||
virtual bool isActive(void);
|
||||
virtual Gui::Action * createAction(void);
|
||||
virtual void activated(int iMsg) override;
|
||||
virtual bool isActive(void) override;
|
||||
virtual Gui::Action * createAction(void) override;
|
||||
private:
|
||||
void updateIcon(bool value);
|
||||
void updateInactiveHandlerIcon();
|
||||
@@ -1213,6 +1189,255 @@ bool CmdSketcherGrid::isActive()
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Snap tool */
|
||||
class SnapSpaceAction : public QWidgetAction
|
||||
{
|
||||
public:
|
||||
SnapSpaceAction(QObject* parent) : QWidgetAction(parent) {
|
||||
setEnabled(false);
|
||||
}
|
||||
|
||||
void updateWidget(bool snapenabled) {
|
||||
|
||||
auto updateCheckBox = [](QCheckBox* checkbox, bool value) {
|
||||
auto checked = checkbox->checkState() == Qt::Checked;
|
||||
|
||||
if (value != checked) {
|
||||
const QSignalBlocker blocker(checkbox);
|
||||
checkbox->setChecked(value);
|
||||
}
|
||||
};
|
||||
|
||||
auto updateSpinBox = [](Gui::QuantitySpinBox* spinbox, double value) {
|
||||
auto currentvalue = spinbox->rawValue();
|
||||
|
||||
if (currentvalue != value) {
|
||||
const QSignalBlocker blocker(spinbox);
|
||||
spinbox->setValue(value);
|
||||
}
|
||||
};
|
||||
|
||||
ParameterGrp::handle hGrp = getParameterPath();
|
||||
|
||||
updateCheckBox(snapToObjects, hGrp->GetBool("SnapToObjects", true));
|
||||
|
||||
updateCheckBox(snapToGrid, hGrp->GetBool("SnapToGrid", false));
|
||||
|
||||
updateSpinBox(snapAngle, hGrp->GetFloat("SnapAngle", 5.0));
|
||||
|
||||
snapToObjects->setEnabled(snapenabled);
|
||||
snapToGrid->setEnabled(snapenabled);
|
||||
angleLabel->setEnabled(snapenabled);
|
||||
snapAngle->setEnabled(snapenabled);
|
||||
}
|
||||
|
||||
void languageChange()
|
||||
{
|
||||
snapToObjects->setText(tr("Snap to objects"));
|
||||
snapToObjects->setToolTip(tr("New points will snap to the currently preselected object. It will also snap to the middle of lines and arcs."));
|
||||
snapToObjects->setStatusTip(snapToObjects->toolTip());
|
||||
|
||||
snapToGrid->setText(tr("Snap to Grid"));
|
||||
snapToGrid->setToolTip(tr("New points will snap to the nearest grid line.\nPoints must be set closer than a fifth of the grid spacing to a grid line to snap."));
|
||||
snapToGrid->setStatusTip(snapToGrid->toolTip());
|
||||
|
||||
angleLabel->setText(tr("Snap angle"));
|
||||
snapAngle->setToolTip(tr("Angular step for tools that use 'Snap at Angle' (line for instance). Hold CTRL to enable 'Snap at Angle'. The angle start from the East axis (horizontal right)"));
|
||||
}
|
||||
|
||||
protected:
|
||||
QWidget* createWidget(QWidget* parent) override
|
||||
{
|
||||
snapToObjects = new QCheckBox();
|
||||
|
||||
snapToGrid = new QCheckBox();
|
||||
|
||||
angleLabel = new QLabel();
|
||||
|
||||
snapAngle = new Gui::QuantitySpinBox();
|
||||
snapAngle->setProperty("unit", QVariant(QStringLiteral("deg")));
|
||||
snapAngle->setObjectName(QStringLiteral("snapAngle"));
|
||||
snapAngle->setMaximum(99999999.0);
|
||||
snapAngle->setMinimum(0);
|
||||
|
||||
QWidget* snapW = new QWidget(parent);
|
||||
auto* layout = new QGridLayout(snapW);
|
||||
layout->addWidget(snapToGrid, 0, 0, 1, 2);
|
||||
layout->addWidget(snapToObjects, 1, 0, 1, 2);
|
||||
layout->addWidget(angleLabel, 2, 0);
|
||||
layout->addWidget(snapAngle, 2, 1);
|
||||
|
||||
languageChange();
|
||||
|
||||
QObject::connect(snapToObjects, &QCheckBox::stateChanged, [this](int state) {
|
||||
ParameterGrp::handle hGrp = this->getParameterPath();
|
||||
hGrp->SetBool("SnapToObjects", state == Qt::Checked);
|
||||
});
|
||||
|
||||
QObject::connect(snapToGrid, &QCheckBox::stateChanged, [this](int state) {
|
||||
ParameterGrp::handle hGrp = this->getParameterPath();
|
||||
hGrp->SetBool("SnapToGrid", state == Qt::Checked);
|
||||
});
|
||||
|
||||
QObject::connect(snapAngle, qOverload<double>(&Gui::QuantitySpinBox::valueChanged), [this](double val) {
|
||||
ParameterGrp::handle hGrp = this->getParameterPath();
|
||||
hGrp->SetFloat("SnapAngle", val);
|
||||
});
|
||||
|
||||
return snapW;
|
||||
}
|
||||
|
||||
private:
|
||||
ParameterGrp::handle getParameterPath() {
|
||||
return App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher/Snap");
|
||||
}
|
||||
|
||||
private:
|
||||
QCheckBox* snapToObjects;
|
||||
QCheckBox* snapToGrid;
|
||||
QLabel* angleLabel;
|
||||
Gui::QuantitySpinBox* snapAngle;
|
||||
};
|
||||
|
||||
class CmdSketcherSnap : public Gui::Command, public ParameterGrp::ObserverType
|
||||
{
|
||||
public:
|
||||
CmdSketcherSnap();
|
||||
virtual ~CmdSketcherSnap();
|
||||
virtual const char* className() const override
|
||||
{
|
||||
return "CmdSketcherSnap";
|
||||
}
|
||||
virtual void languageChange() override;
|
||||
|
||||
void OnChange(Base::Subject<const char*> &rCaller, const char * sReason) override;
|
||||
protected:
|
||||
virtual void activated(int iMsg) override;
|
||||
virtual bool isActive(void) override;
|
||||
virtual Gui::Action* createAction(void) override;
|
||||
private:
|
||||
void updateIcon(bool value);
|
||||
|
||||
ParameterGrp::handle getParameterPath() {
|
||||
return App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher/Snap");
|
||||
}
|
||||
|
||||
CmdSketcherSnap(const CmdSketcherSnap&) = delete;
|
||||
CmdSketcherSnap(CmdSketcherSnap&&) = delete;
|
||||
CmdSketcherSnap& operator= (const CmdSketcherSnap&) = delete;
|
||||
CmdSketcherSnap& operator= (CmdSketcherSnap&&) = delete;
|
||||
|
||||
bool snapEnabled;
|
||||
};
|
||||
|
||||
CmdSketcherSnap::CmdSketcherSnap()
|
||||
: Command("Sketcher_Snap")
|
||||
{
|
||||
sAppModule = "Sketcher";
|
||||
sGroup = "Sketcher";
|
||||
sMenuText = QT_TR_NOOP("Toggle Snap");
|
||||
sToolTipText = QT_TR_NOOP("Toggle all snapping functionalities. In the menu you can toggle individually 'Snap to Grid', 'Snap to Objects' and further snap settings");
|
||||
sWhatsThis = "Sketcher_Snap";
|
||||
sStatusTip = sToolTipText;
|
||||
eType = 0;
|
||||
|
||||
ParameterGrp::handle hGrp = this->getParameterPath();
|
||||
hGrp->Attach(this);
|
||||
}
|
||||
|
||||
CmdSketcherSnap::~CmdSketcherSnap() {
|
||||
|
||||
ParameterGrp::handle hGrp = this->getParameterPath();
|
||||
hGrp->Detach(this);
|
||||
}
|
||||
|
||||
void CmdSketcherSnap::OnChange(Base::Subject<const char*> &rCaller, const char * sReason)
|
||||
{
|
||||
Q_UNUSED(rCaller)
|
||||
|
||||
if (strcmp(sReason, "Snap") == 0) {
|
||||
snapEnabled = getParameterPath()->GetBool("Snap", true);
|
||||
}
|
||||
}
|
||||
|
||||
void CmdSketcherSnap::updateIcon(bool value)
|
||||
{
|
||||
static QIcon active = Gui::BitmapFactory().iconFromTheme("Sketcher_Snap");
|
||||
static QIcon inactive = Gui::BitmapFactory().iconFromTheme("Sketcher_Snap_Deactivated");
|
||||
|
||||
auto* pcAction = qobject_cast<Gui::ActionGroup*>(getAction());
|
||||
pcAction->setIcon(value ? active : inactive);
|
||||
}
|
||||
|
||||
void CmdSketcherSnap::activated(int iMsg)
|
||||
{
|
||||
Q_UNUSED(iMsg);
|
||||
|
||||
getParameterPath()->SetBool("Snap", !snapEnabled);
|
||||
|
||||
// snapEnable updated via observer
|
||||
updateIcon(snapEnabled);
|
||||
|
||||
//Update the widget :
|
||||
if (!_pcAction)
|
||||
return;
|
||||
|
||||
Gui::ActionGroup* pcAction = qobject_cast<Gui::ActionGroup*>(_pcAction);
|
||||
QList<QAction*> a = pcAction->actions();
|
||||
|
||||
auto* ssa = static_cast<SnapSpaceAction*>(a[0]);
|
||||
ssa->updateWidget(snapEnabled);
|
||||
}
|
||||
|
||||
Gui::Action* CmdSketcherSnap::createAction()
|
||||
{
|
||||
auto* pcAction = new Gui::ActionGroup(this, Gui::getMainWindow());
|
||||
pcAction->setDropDownMenu(true);
|
||||
pcAction->setExclusive(false);
|
||||
applyCommandData(this->className(), pcAction);
|
||||
|
||||
SnapSpaceAction* ssa = new SnapSpaceAction(pcAction);
|
||||
pcAction->addAction(ssa);
|
||||
|
||||
_pcAction = pcAction;
|
||||
|
||||
QObject::connect(pcAction, &Gui::ActionGroup::aboutToShow, [ssa, this](QMenu* menu) {
|
||||
Q_UNUSED(menu)
|
||||
ssa->updateWidget(snapEnabled);
|
||||
});
|
||||
|
||||
// set the right pixmap
|
||||
updateIcon(snapEnabled);
|
||||
|
||||
return pcAction;
|
||||
}
|
||||
|
||||
void CmdSketcherSnap::languageChange()
|
||||
{
|
||||
Command::languageChange();
|
||||
|
||||
if (!_pcAction)
|
||||
return;
|
||||
|
||||
Gui::ActionGroup* pcAction = qobject_cast<Gui::ActionGroup*>(_pcAction);
|
||||
QList<QAction*> a = pcAction->actions();
|
||||
|
||||
auto* ssa = static_cast<SnapSpaceAction*>(a[0]);
|
||||
ssa->languageChange();
|
||||
}
|
||||
|
||||
bool CmdSketcherSnap::isActive()
|
||||
{
|
||||
auto* vp = getInactiveHandlerEditModeSketchViewProvider();
|
||||
|
||||
if (vp) {
|
||||
updateIcon(snapEnabled);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CreateSketcherCommands()
|
||||
{
|
||||
@@ -1230,4 +1455,5 @@ void CreateSketcherCommands()
|
||||
rcCmdMgr.addCommand(new CmdSketcherMergeSketches());
|
||||
rcCmdMgr.addCommand(new CmdSketcherViewSection());
|
||||
rcCmdMgr.addCommand(new CmdSketcherGrid());
|
||||
rcCmdMgr.addCommand(new CmdSketcherSnap());
|
||||
}
|
||||
|
||||
@@ -2150,7 +2150,7 @@ CmdSketcherConstrainDistance::CmdSketcherConstrainDistance()
|
||||
sAppModule = "Sketcher";
|
||||
sGroup = "Sketcher";
|
||||
sMenuText = QT_TR_NOOP("Constrain distance");
|
||||
sToolTipText = QT_TR_NOOP("Fix a length of a line or the distance between a line and a vertex");
|
||||
sToolTipText = QT_TR_NOOP("Fix a length of a line or the distance between a line and a vertex or between two circles");
|
||||
sWhatsThis = "Sketcher_ConstrainDistance";
|
||||
sStatusTip = sToolTipText;
|
||||
sPixmap = "Constraint_Length";
|
||||
@@ -2160,7 +2160,8 @@ CmdSketcherConstrainDistance::CmdSketcherConstrainDistance()
|
||||
allowedSelSequences = {{SelVertex, SelVertexOrRoot}, {SelRoot, SelVertex},
|
||||
{SelEdge}, {SelExternalEdge},
|
||||
{SelVertex, SelEdgeOrAxis}, {SelRoot, SelEdge},
|
||||
{SelVertex, SelExternalEdge}, {SelRoot, SelExternalEdge}};
|
||||
{SelVertex, SelExternalEdge}, {SelRoot, SelExternalEdge},
|
||||
{SelEdge, SelEdge}};
|
||||
}
|
||||
|
||||
void CmdSketcherConstrainDistance::activated(int iMsg)
|
||||
@@ -2289,6 +2290,55 @@ void CmdSketcherConstrainDistance::activated(int iMsg)
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (isEdge(GeoId1,PosId1) && isEdge(GeoId2,PosId2)) { // circle to circle distance
|
||||
const Part::Geometry *geom1 = Obj->getGeometry(GeoId1);
|
||||
const Part::Geometry *geom2 = Obj->getGeometry(GeoId2);
|
||||
if (geom1->getTypeId() == Part::GeomCircle::getClassTypeId()
|
||||
&& geom2->getTypeId() == Part::GeomCircle::getClassTypeId() ) {
|
||||
auto circleSeg1 = static_cast<const Part::GeomCircle*>(geom1);
|
||||
double radius1 = circleSeg1->getRadius();
|
||||
Base::Vector3d center1 = circleSeg1->getCenter();
|
||||
|
||||
auto circleSeg2 = static_cast<const Part::GeomCircle*>(geom2);
|
||||
double radius2 = circleSeg2->getRadius();
|
||||
Base::Vector3d center2 = circleSeg2->getCenter();
|
||||
|
||||
double ActDist = 0.;
|
||||
|
||||
Base::Vector3d intercenter = center1 - center2;
|
||||
double intercenterdistance = intercenter.Length();
|
||||
|
||||
if( intercenterdistance >= radius1 &&
|
||||
intercenterdistance >= radius2 ) {
|
||||
|
||||
ActDist = intercenterdistance - radius1 - radius2;
|
||||
}
|
||||
else {
|
||||
double bigradius = std::max(radius1,radius2);
|
||||
double smallradius = std::min(radius1,radius2);
|
||||
|
||||
ActDist = bigradius - smallradius - intercenterdistance;
|
||||
}
|
||||
|
||||
openCommand(QT_TRANSLATE_NOOP("Command", "Add circle to circle distance constraint"));
|
||||
Gui::cmdAppObjectArgs(selection[0].getObject(),
|
||||
"addConstraint(Sketcher.Constraint('Distance',%d,%d,%f)) ",
|
||||
GeoId1,GeoId2,ActDist);
|
||||
|
||||
if (arebothpointsorsegmentsfixed || constraintCreationMode==Reference) { // it is a constraint on a external line, make it non-driving
|
||||
const std::vector<Sketcher::Constraint *> &ConStr = Obj->Constraints.getValues();
|
||||
|
||||
Gui::cmdAppObjectArgs(selection[0].getObject(),
|
||||
"setDriving(%i,%s)",
|
||||
ConStr.size()-1,"False");
|
||||
finishDatumConstraint (this, Obj, false);
|
||||
}
|
||||
else
|
||||
finishDatumConstraint (this, Obj, true);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (isEdge(GeoId1,PosId1)) { // line length
|
||||
if (GeoId1 < 0 && GeoId1 >= Sketcher::GeoEnum::VAxis) {
|
||||
Gui::TranslatedNotification(Obj,
|
||||
@@ -2327,7 +2377,7 @@ void CmdSketcherConstrainDistance::activated(int iMsg)
|
||||
|
||||
Gui::TranslatedNotification(Obj,
|
||||
QObject::tr("Wrong selection"),
|
||||
QObject::tr("Select exactly one line or one point and one line or two points from the sketch."));
|
||||
QObject::tr("Select exactly one line or one point and one line or two points or two circles from the sketch."));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2416,6 +2466,9 @@ void CmdSketcherConstrainDistance::applyConstraint(std::vector<SelIdPair> &selSe
|
||||
else
|
||||
finishDatumConstraint (this, Obj, true);
|
||||
}
|
||||
else if (geom->getTypeId() == Part::GeomCircle::getClassTypeId()) {
|
||||
// allow this selection but do nothing as it needs 2 circles
|
||||
}
|
||||
else {
|
||||
Gui::TranslatedNotification(Obj,
|
||||
QObject::tr("Wrong selection"),
|
||||
@@ -2460,6 +2513,61 @@ void CmdSketcherConstrainDistance::applyConstraint(std::vector<SelIdPair> &selSe
|
||||
|
||||
return;
|
||||
}
|
||||
case 8: // {SelEdge, SelEdge}
|
||||
{
|
||||
GeoId1 = selSeq.at(0).GeoId; GeoId2 = selSeq.at(1).GeoId;
|
||||
const Part::Geometry *geom1 = Obj->getGeometry(GeoId1);
|
||||
const Part::Geometry *geom2 = Obj->getGeometry(GeoId2);
|
||||
if (geom1->getTypeId() == Part::GeomCircle::getClassTypeId()
|
||||
&& geom2->getTypeId() == Part::GeomCircle::getClassTypeId() ) { // circle to circle distance
|
||||
auto circleSeg1 = static_cast<const Part::GeomCircle*>(geom1);
|
||||
double radius1 = circleSeg1->getRadius();
|
||||
Base::Vector3d center1 = circleSeg1->getCenter();
|
||||
|
||||
auto circleSeg2 = static_cast<const Part::GeomCircle*>(geom2);
|
||||
double radius2 = circleSeg2->getRadius();
|
||||
Base::Vector3d center2 = circleSeg2->getCenter();
|
||||
|
||||
double ActDist = 0.;
|
||||
|
||||
Base::Vector3d intercenter = center1 - center2;
|
||||
double intercenterdistance = intercenter.Length();
|
||||
|
||||
if( intercenterdistance >= radius1 &&
|
||||
intercenterdistance >= radius2 ) {
|
||||
|
||||
ActDist = intercenterdistance - radius1 - radius2;
|
||||
}
|
||||
else {
|
||||
double bigradius = std::max(radius1,radius2);
|
||||
double smallradius = std::min(radius1,radius2);
|
||||
|
||||
ActDist = bigradius - smallradius - intercenterdistance;
|
||||
}
|
||||
|
||||
openCommand(QT_TRANSLATE_NOOP("Command", "Add circle to circle distance constraint"));
|
||||
Gui::cmdAppObjectArgs(Obj,
|
||||
"addConstraint(Sketcher.Constraint('Distance',%d,%d,%f)) ",
|
||||
GeoId1,GeoId2,ActDist);
|
||||
|
||||
if (arebothpointsorsegmentsfixed || constraintCreationMode==Reference) { // it is a constraint on a external line, make it non-driving
|
||||
const std::vector<Sketcher::Constraint *> &ConStr = Obj->Constraints.getValues();
|
||||
|
||||
Gui::cmdAppObjectArgs(Obj, "setDriving(%i,%s)",
|
||||
ConStr.size()-1,"False");
|
||||
finishDatumConstraint (this, Obj, false);
|
||||
}
|
||||
else
|
||||
finishDatumConstraint (this, Obj, true);
|
||||
|
||||
return;
|
||||
} else {
|
||||
Gui::TranslatedNotification(Obj,
|
||||
QObject::tr("Wrong selection"),
|
||||
QObject::tr("Select exactly one line or one point and one line or two points or two circles from the sketch."));
|
||||
|
||||
}
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -114,6 +114,11 @@ inline int ViewProviderSketchDrawSketchHandlerAttorney::getPreselectCross(const
|
||||
return vp.getPreselectCross();
|
||||
}
|
||||
|
||||
inline void ViewProviderSketchDrawSketchHandlerAttorney::setAngleSnapping(ViewProviderSketch &vp, bool enable, Base::Vector2d referencePoint)
|
||||
{
|
||||
vp.setAngleSnapping(enable, referencePoint);
|
||||
}
|
||||
|
||||
|
||||
/**************************** CurveConverter **********************************************/
|
||||
|
||||
@@ -250,6 +255,7 @@ void DrawSketchHandler::deactivate()
|
||||
drawEditMarkers(std::vector<Base::Vector2d>());
|
||||
resetPositionText();
|
||||
unsetCursor();
|
||||
setAngleSnapping(false);
|
||||
}
|
||||
|
||||
void DrawSketchHandler::preActivated()
|
||||
@@ -992,3 +998,7 @@ Sketcher::SketchObject * DrawSketchHandler::getSketchObject()
|
||||
return sketchgui->getSketchObject();
|
||||
}
|
||||
|
||||
void DrawSketchHandler::setAngleSnapping(bool enable, Base::Vector2d referencePoint)
|
||||
{
|
||||
ViewProviderSketchDrawSketchHandlerAttorney::setAngleSnapping(*sketchgui, enable, referencePoint);
|
||||
}
|
||||
@@ -86,11 +86,13 @@ private:
|
||||
static inline void setAxisPickStyle(ViewProviderSketch &vp, bool on);
|
||||
static inline void moveCursorToSketchPoint(ViewProviderSketch &vp, Base::Vector2d point);
|
||||
static inline void preselectAtPoint(ViewProviderSketch &vp, Base::Vector2d point);
|
||||
static inline void setAngleSnapping(ViewProviderSketch &vp, bool enable, Base::Vector2d referencePoint = Base::Vector2d(0., 0.));
|
||||
|
||||
static inline int getPreselectPoint(const ViewProviderSketch &vp);
|
||||
static inline int getPreselectCurve(const ViewProviderSketch &vp);
|
||||
static inline int getPreselectCross(const ViewProviderSketch &vp);
|
||||
|
||||
|
||||
friend class DrawSketchHandler;
|
||||
};
|
||||
|
||||
@@ -203,6 +205,8 @@ protected:
|
||||
|
||||
Sketcher::SketchObject * getSketchObject();
|
||||
|
||||
void setAngleSnapping(bool enable, Base::Vector2d referencePoint = Base::Vector2d(0., 0.));
|
||||
|
||||
private:
|
||||
void setSvgCursor(const QString &svgName, int x, int y,
|
||||
const std::map<unsigned long, unsigned long>& colorMapping = std::map<unsigned long, unsigned long>());
|
||||
|
||||
@@ -131,6 +131,7 @@ public:
|
||||
CenterPoint = onSketchPos;
|
||||
EditCurve.resize(34);
|
||||
EditCurve[0] = onSketchPos;
|
||||
setAngleSnapping(true, EditCurve[0]);
|
||||
Mode = STATUS_SEEK_Second;
|
||||
}
|
||||
else if (Mode==STATUS_SEEK_Second){
|
||||
@@ -158,6 +159,7 @@ public:
|
||||
|
||||
drawEdit(EditCurve);
|
||||
applyCursor();
|
||||
setAngleSnapping(false);
|
||||
Mode = STATUS_End;
|
||||
}
|
||||
|
||||
|
||||
@@ -176,6 +176,7 @@ public:
|
||||
if (Mode==STATUS_SEEK_First){
|
||||
EditCurve[0] = onSketchPos;
|
||||
centerPoint = onSketchPos;
|
||||
setAngleSnapping(true, centerPoint);
|
||||
Mode = STATUS_SEEK_Second;
|
||||
}
|
||||
else if(Mode==STATUS_SEEK_Second) {
|
||||
@@ -192,6 +193,7 @@ public:
|
||||
else { // Fourth
|
||||
endPoint = onSketchPos;
|
||||
|
||||
setAngleSnapping(false);
|
||||
Mode = STATUS_Close;
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -216,10 +216,12 @@ public:
|
||||
if (method == PERIAPSIS_APOAPSIS_B) {
|
||||
if (mode == STATUS_SEEK_PERIAPSIS) {
|
||||
periapsis = onSketchPos;
|
||||
setAngleSnapping(true, periapsis);
|
||||
mode = STATUS_SEEK_APOAPSIS;
|
||||
}
|
||||
else if (mode == STATUS_SEEK_APOAPSIS) {
|
||||
apoapsis = onSketchPos;
|
||||
setAngleSnapping(false);
|
||||
mode = STATUS_SEEK_B;
|
||||
}
|
||||
else {
|
||||
@@ -228,10 +230,12 @@ public:
|
||||
} else { // method is CENTER_PERIAPSIS_B
|
||||
if (mode == STATUS_SEEK_CENTROID) {
|
||||
centroid = onSketchPos;
|
||||
setAngleSnapping(true, centroid);
|
||||
mode = STATUS_SEEK_PERIAPSIS;
|
||||
}
|
||||
else if (mode == STATUS_SEEK_PERIAPSIS) {
|
||||
periapsis = onSketchPos;
|
||||
setAngleSnapping(false);
|
||||
mode = STATUS_SEEK_B;
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -76,11 +76,13 @@ public:
|
||||
if (Mode==STATUS_SEEK_First){
|
||||
EditCurve[0] = onSketchPos;
|
||||
|
||||
setAngleSnapping(true, EditCurve[0]);
|
||||
Mode = STATUS_SEEK_Second;
|
||||
}
|
||||
else {
|
||||
EditCurve[1] = onSketchPos;
|
||||
drawEdit(EditCurve);
|
||||
setAngleSnapping(false);
|
||||
Mode = STATUS_End;
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -62,6 +62,7 @@
|
||||
#include "SoZoomTranslation.h"
|
||||
#include "ViewProviderSketch.h"
|
||||
#include "ViewProviderSketchCoinAttorney.h"
|
||||
#include "Utils.h"
|
||||
|
||||
|
||||
using namespace SketcherGui;
|
||||
@@ -654,24 +655,33 @@ Restart:
|
||||
if (Constr->SecondPos != Sketcher::PointPos::none) { // point to point distance
|
||||
pnt1 = geolistfacade.getPoint(Constr->First, Constr->FirstPos);
|
||||
pnt2 = geolistfacade.getPoint(Constr->Second, Constr->SecondPos);
|
||||
} else if (Constr->Second != GeoEnum::GeoUndef) { // point to line distance
|
||||
} else if (Constr->Second != GeoEnum::GeoUndef) {
|
||||
pnt1 = geolistfacade.getPoint(Constr->First, Constr->FirstPos);
|
||||
|
||||
const Part::Geometry *geo = geolistfacade.getGeometryFromGeoId(Constr->Second);
|
||||
if (geo->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
|
||||
if (geo->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { // point to line distance
|
||||
const Part::GeomLineSegment *lineSeg = static_cast<const Part::GeomLineSegment *>(geo);
|
||||
Base::Vector3d l2p1 = lineSeg->getStartPoint();
|
||||
Base::Vector3d l2p2 = lineSeg->getEndPoint();
|
||||
// calculate the projection of p1 onto line2
|
||||
pnt2.ProjectToLine(pnt1-l2p1, l2p2-l2p1);
|
||||
pnt2 += pnt1;
|
||||
|
||||
} else if (geo->getTypeId() == Part::GeomCircle::getClassTypeId()) { // circle to circle distance
|
||||
const Part::Geometry *geo1 = geolistfacade.getGeometryFromGeoId(Constr->First);
|
||||
if (geo1->getTypeId() == Part::GeomCircle::getClassTypeId()) {
|
||||
const Part::GeomCircle *circleSeg1 = static_cast<const Part::GeomCircle*>(geo1);
|
||||
auto circleSeg2 = static_cast<const Part::GeomCircle*>(geo);
|
||||
GetCirclesMinimalDistance(circleSeg1, circleSeg2, pnt1, pnt2);
|
||||
}
|
||||
|
||||
} else
|
||||
break;
|
||||
} else if (Constr->FirstPos != Sketcher::PointPos::none) {
|
||||
pnt2 = geolistfacade.getPoint(Constr->First, Constr->FirstPos);
|
||||
} else if (Constr->First != GeoEnum::GeoUndef) {
|
||||
const Part::Geometry *geo = geolistfacade.getGeometryFromGeoId(Constr->First);
|
||||
if (geo->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
|
||||
if (geo->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { // segment distance
|
||||
const Part::GeomLineSegment *lineSeg = static_cast<const Part::GeomLineSegment *>(geo);
|
||||
pnt1 = lineSeg->getStartPoint();
|
||||
pnt2 = lineSeg->getEndPoint();
|
||||
|
||||
@@ -106,6 +106,8 @@
|
||||
<file>icons/general/Sketcher_ViewSketch.svg</file>
|
||||
<file>icons/general/Sketcher_GridToggle.svg</file>
|
||||
<file>icons/general/Sketcher_GridToggle_Deactivated.svg</file>
|
||||
<file>icons/general/Sketcher_Snap.svg</file>
|
||||
<file>icons/general/Sketcher_Snap_Deactivated.svg</file>
|
||||
</qresource>
|
||||
<qresource>
|
||||
<file>icons/geometry/Sketcher_AlterFillet.svg</file>
|
||||
|
||||
@@ -0,0 +1,386 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="64px"
|
||||
height="64px"
|
||||
id="svg3364"
|
||||
version="1.1"
|
||||
sodipodi:docname="Sketcher_Snap.svg"
|
||||
inkscape:version="1.1-beta1 (77e7b44db3, 2021-03-28)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<sodipodi:namedview
|
||||
id="namedview55"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
objecttolerance="10.0"
|
||||
gridtolerance="10.0"
|
||||
guidetolerance="10.0"
|
||||
inkscape:pageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
showgrid="false"
|
||||
inkscape:zoom="11.765625"
|
||||
inkscape:cx="39.861886"
|
||||
inkscape:cy="27.580345"
|
||||
inkscape:window-width="1725"
|
||||
inkscape:window-height="1013"
|
||||
inkscape:window-x="1213"
|
||||
inkscape:window-y="293"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg3364" />
|
||||
<defs
|
||||
id="defs3366">
|
||||
<linearGradient
|
||||
id="linearGradient3864">
|
||||
<stop
|
||||
id="stop3866"
|
||||
offset="0"
|
||||
style="stop-color:#71b2f8;stop-opacity:1" />
|
||||
<stop
|
||||
id="stop3868"
|
||||
offset="1"
|
||||
style="stop-color:#002795;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient3864"
|
||||
id="radialGradient2571"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
cx="342.58258"
|
||||
cy="27.256668"
|
||||
fx="342.58258"
|
||||
fy="27.256668"
|
||||
r="19.571428"
|
||||
gradientTransform="matrix(1.6258409,0.5434973,-8.8819886e-2,0.2656996,-215.02413,-170.90186)" />
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient3593"
|
||||
id="radialGradient3352"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
cx="345.28433"
|
||||
cy="15.560534"
|
||||
fx="345.28433"
|
||||
fy="15.560534"
|
||||
r="19.571428"
|
||||
gradientTransform="translate(-0.1767767,-2.6516504)" />
|
||||
<linearGradient
|
||||
id="linearGradient3593">
|
||||
<stop
|
||||
style="stop-color:#c8e0f9;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop3595" />
|
||||
<stop
|
||||
style="stop-color:#637dca;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3597" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient3593"
|
||||
id="radialGradient3354"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
cx="330.63791"
|
||||
cy="39.962704"
|
||||
fx="330.63791"
|
||||
fy="39.962704"
|
||||
r="19.571428"
|
||||
gradientTransform="translate(-0.1767767,-2.6516504)" />
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient3864"
|
||||
id="radialGradient3369"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.6258409,0.5434973,-8.8819886e-2,0.2656996,-461.81066,-173.06271)"
|
||||
cx="342.58258"
|
||||
cy="27.256668"
|
||||
fx="342.58258"
|
||||
fy="27.256668"
|
||||
r="19.571428" />
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient3593"
|
||||
id="radialGradient3372"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.0012324,0,0,0.9421773,-327.50313,-4.3316646)"
|
||||
cx="345.28433"
|
||||
cy="15.560534"
|
||||
fx="345.28433"
|
||||
fy="15.560534"
|
||||
r="19.571428" />
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient3593"
|
||||
id="radialGradient3375"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.0012324,0,0,0.9421773,-287.81791,-28.143054)"
|
||||
cx="330.63791"
|
||||
cy="39.962704"
|
||||
fx="330.63791"
|
||||
fy="39.962704"
|
||||
r="19.571428" />
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient3864"
|
||||
id="radialGradient3380"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.9829174,1.3240854,-1.2330051,0.8105158,-131.04134,-483.74563)"
|
||||
cx="320.44025"
|
||||
cy="113.23357"
|
||||
fx="320.44025"
|
||||
fy="113.23357"
|
||||
r="19.571428" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient3864"
|
||||
id="linearGradient3914"
|
||||
x1="6.94525"
|
||||
y1="36.838673"
|
||||
x2="48.691113"
|
||||
y2="36.838673"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.0012324,0,0,0.9421773,-4.8699606,-2.3863162)" />
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient3794"
|
||||
id="radialGradient3800"
|
||||
cx="1"
|
||||
cy="45"
|
||||
fx="1"
|
||||
fy="45"
|
||||
r="41"
|
||||
gradientTransform="matrix(0.93348213,-2.2905276e-8,0,0.28687573,0.06651751,32.090592)"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
id="linearGradient3794">
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop3796" />
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:0"
|
||||
offset="1"
|
||||
id="stop3798" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
gradientTransform="translate(0,-9)"
|
||||
xlink:href="#linearGradient3777"
|
||||
id="linearGradient3783"
|
||||
x1="53.896763"
|
||||
y1="51.179787"
|
||||
x2="48"
|
||||
y2="32"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
id="linearGradient3777">
|
||||
<stop
|
||||
style="stop-color:#204a87;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop3779" />
|
||||
<stop
|
||||
style="stop-color:#3465a4;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3781" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
gradientTransform="translate(22,-17)"
|
||||
xlink:href="#linearGradient3767"
|
||||
id="linearGradient3773"
|
||||
x1="22.116516"
|
||||
y1="55.717518"
|
||||
x2="19"
|
||||
y2="33"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
id="linearGradient3767">
|
||||
<stop
|
||||
style="stop-color:#3465a4;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop3769" />
|
||||
<stop
|
||||
style="stop-color:#729fcf;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3771" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
gradientTransform="translate(-2,-11)"
|
||||
xlink:href="#linearGradient3777-6"
|
||||
id="linearGradient3783-3"
|
||||
x1="53.896763"
|
||||
y1="51.179787"
|
||||
x2="48"
|
||||
y2="32"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
id="linearGradient3777-6">
|
||||
<stop
|
||||
style="stop-color:#204a87;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop3779-7" />
|
||||
<stop
|
||||
style="stop-color:#3465a4;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3781-5" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
y2="32"
|
||||
x2="48"
|
||||
y1="51.179787"
|
||||
x1="53.896763"
|
||||
gradientTransform="translate(-24,-13)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3066"
|
||||
xlink:href="#linearGradient3777-6" />
|
||||
<linearGradient
|
||||
y2="5"
|
||||
x2="-22"
|
||||
y1="18"
|
||||
x1="-18"
|
||||
gradientTransform="matrix(0.53559025,0,0,0.53556875,-6.4812797,2.8041108)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3323-4"
|
||||
xlink:href="#linearGradient3836-9-3-7-2-7-7-2-6" />
|
||||
<linearGradient
|
||||
id="linearGradient3836-9-3-7-2-7-7-2-6">
|
||||
<stop
|
||||
style="stop-color:#a40000;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop3838-8-5-4-4-6-4-4-61" />
|
||||
<stop
|
||||
style="stop-color:#ef2929;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3840-1-6-0-5-1-0-5-8" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3836-9-3-7-2-7-7-2-6"
|
||||
id="linearGradient898"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.53559025,0,0,0.53556875,-6.4812797,2.8041108)"
|
||||
x1="-18"
|
||||
y1="18"
|
||||
x2="-22"
|
||||
y2="5" />
|
||||
<linearGradient
|
||||
y2="5"
|
||||
x2="-22"
|
||||
y1="18"
|
||||
x1="-18"
|
||||
gradientTransform="matrix(0.53559025,0,0,0.53556875,-6.4812797,2.8041108)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3323-3"
|
||||
xlink:href="#linearGradient3836-9-3-7-2-7-7-2-6" />
|
||||
</defs>
|
||||
<metadata
|
||||
id="metadata3369">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>[wmayer]</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
<dc:date>2011-10-10</dc:date>
|
||||
<dc:relation>http://www.freecadweb.org/wiki/index.php?title=Artwork</dc:relation>
|
||||
<dc:publisher>
|
||||
<cc:Agent>
|
||||
<dc:title>FreeCAD</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:publisher>
|
||||
<dc:identifier>FreeCAD/src/Mod/Part/Gui/Resources/icons/Part_Section.svg</dc:identifier>
|
||||
<dc:rights>
|
||||
<cc:Agent>
|
||||
<dc:title>FreeCAD LGPL2+</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:rights>
|
||||
<cc:license>https://www.gnu.org/copyleft/lesser.html</cc:license>
|
||||
<dc:contributor>
|
||||
<cc:Agent>
|
||||
<dc:title>[agryson] Alexander Gryson</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:contributor>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<path
|
||||
style="fill:none;stroke:#363636;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:6, 3;stroke-dashoffset:0;stroke-opacity:1"
|
||||
d="M 2.3798139,32.218121 H 61.620186"
|
||||
id="path985-9" />
|
||||
<path
|
||||
style="fill:none;stroke:#363636;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:6, 3;stroke-dashoffset:0;stroke-opacity:1"
|
||||
d="M 2.2324867,51.127858 H 61.472858"
|
||||
id="path985-6" />
|
||||
<path
|
||||
style="fill:none;stroke:#4ce642;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:6, 3;stroke-dashoffset:0;stroke-opacity:1"
|
||||
d="M 12.221742,61.620185 V 2.3798148"
|
||||
id="path985-92" />
|
||||
<path
|
||||
style="fill:none;stroke:#363636;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:6, 3;stroke-dashoffset:0;stroke-opacity:1"
|
||||
d="M 31.131479,61.620186 V 2.3798143"
|
||||
id="path985-9-8" />
|
||||
<path
|
||||
style="fill:none;stroke:#363636;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:6, 3;stroke-dashoffset:0;stroke-opacity:1"
|
||||
d="M 50.041216,61.620185 V 2.3798148"
|
||||
id="path985-6-2" />
|
||||
<path
|
||||
style="fill:none;stroke:#4ce642;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:6, 3;stroke-dashoffset:0;stroke-opacity:1"
|
||||
d="M 2.2324859,13.308385 H 61.472857"
|
||||
id="path985" />
|
||||
<g
|
||||
transform="rotate(63.326802,46.528624,-2.5779449)"
|
||||
id="g3529-7">
|
||||
<path
|
||||
id="path3491-7"
|
||||
d="M 56,8 V 32"
|
||||
style="fill:none;stroke:#172a04;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
<path
|
||||
id="path3491-4-7"
|
||||
d="M 56,8 V 32"
|
||||
style="fill:none;stroke:#73d216;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
<path
|
||||
id="path3491-4-1-3"
|
||||
d="M 55,8 V 32"
|
||||
style="fill:none;stroke:#8ae234;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
</g>
|
||||
<g
|
||||
style="stroke-width:6.44377;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
transform="matrix(0.35922387,0.71507028,-0.71505986,0.35921864,53.759702,19.45672)"
|
||||
id="g3797-7-2-9-5-4-9-5-96">
|
||||
<path
|
||||
style="fill:#ef2929;stroke:#280000;stroke-width:2.4993;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4250-6-9-4-1-2-4-1-3"
|
||||
d="m -21.570202,4.8706993 a 6.2460052,6.2456584 0.01677682 1 1 9.488239,8.1250407 6.2460052,6.2456584 0.01677682 1 1 -9.488239,-8.1250407 z" />
|
||||
<path
|
||||
style="fill:url(#linearGradient898);fill-opacity:1;stroke:#ef2929;stroke-width:2.4993;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4250-7-0-1-8-7-3-8-7-7"
|
||||
d="m -19.674789,6.4961011 a 3.7491316,3.7489813 0 1 1 5.695246,4.8771099 3.7491316,3.7489813 0 0 1 -5.695246,-4.8771099 z" />
|
||||
</g>
|
||||
<g
|
||||
style="stroke-width:6.44377;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
transform="matrix(0.35922387,0.71507028,-0.71505986,0.35921864,32.313746,30.230346)"
|
||||
id="g3797-7-2-9-5-4-9-5-96-5">
|
||||
<path
|
||||
style="fill:#ef2929;stroke:#280000;stroke-width:2.4993;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4250-6-9-4-1-2-4-1-3-7"
|
||||
d="m -21.570202,4.8706993 a 6.2460052,6.2456584 0.01677682 1 1 9.488239,8.1250407 6.2460052,6.2456584 0.01677682 1 1 -9.488239,-8.1250407 z" />
|
||||
<path
|
||||
style="fill:url(#linearGradient3323-4);fill-opacity:1;stroke:#ef2929;stroke-width:2.4993;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4250-7-0-1-8-7-3-8-7-7-5"
|
||||
d="m -19.674789,6.4961011 a 3.7491316,3.7489813 0 1 1 5.695246,4.8771099 3.7491316,3.7489813 0 0 1 -5.695246,-4.8771099 z" />
|
||||
</g>
|
||||
<g
|
||||
id="g4273"
|
||||
transform="translate(-89.528258,0.54055521)">
|
||||
<path
|
||||
style="fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 108.96148,19.97344 c 4.28933,11.161025 8.9416,20.384136 14.87384,32.552457 l 5.94954,-11.134131 12.749,12.154051 c 3.39402,0.638464 5.31688,-1.489816 4.75963,-4.504649 l -13.00398,-11.814076 8.49933,-6.629483 z"
|
||||
id="path1052"
|
||||
sodipodi:nodetypes="cccccccc" />
|
||||
<path
|
||||
style="fill:#d3d7cf;fill-opacity:1;stroke:#ffffff;stroke-width:1.285;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 112.59215,23.233162 c 3.72959,10.062766 6.25342,13.924184 11.41153,24.895126 l 5.26419,-9.586262 13.8638,13.109482 c 1.42608,0.530564 2.84842,-0.286358 2.07091,-2.088013 l -13.80942,-12.277572 7.29489,-5.92068 z"
|
||||
id="path1052-8"
|
||||
sodipodi:nodetypes="cccccccc" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 14 KiB |
@@ -0,0 +1,377 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="64px"
|
||||
height="64px"
|
||||
id="svg3364"
|
||||
version="1.1"
|
||||
sodipodi:docname="Sketcher_Snap_Deactivated.svg"
|
||||
inkscape:version="1.1-beta1 (77e7b44db3, 2021-03-28)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<sodipodi:namedview
|
||||
id="namedview55"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
objecttolerance="10.0"
|
||||
gridtolerance="10.0"
|
||||
guidetolerance="10.0"
|
||||
inkscape:pageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
showgrid="false"
|
||||
inkscape:zoom="5.8828125"
|
||||
inkscape:cx="56.180611"
|
||||
inkscape:cy="32.63745"
|
||||
inkscape:window-width="1720"
|
||||
inkscape:window-height="1112"
|
||||
inkscape:window-x="743"
|
||||
inkscape:window-y="334"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg3364" />
|
||||
<defs
|
||||
id="defs3366">
|
||||
<linearGradient
|
||||
id="linearGradient3864">
|
||||
<stop
|
||||
id="stop3866"
|
||||
offset="0"
|
||||
style="stop-color:#71b2f8;stop-opacity:1" />
|
||||
<stop
|
||||
id="stop3868"
|
||||
offset="1"
|
||||
style="stop-color:#002795;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient3864"
|
||||
id="radialGradient2571"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
cx="342.58258"
|
||||
cy="27.256668"
|
||||
fx="342.58258"
|
||||
fy="27.256668"
|
||||
r="19.571428"
|
||||
gradientTransform="matrix(1.6258409,0.5434973,-8.8819886e-2,0.2656996,-215.02413,-170.90186)" />
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient3593"
|
||||
id="radialGradient3352"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
cx="345.28433"
|
||||
cy="15.560534"
|
||||
fx="345.28433"
|
||||
fy="15.560534"
|
||||
r="19.571428"
|
||||
gradientTransform="translate(-0.1767767,-2.6516504)" />
|
||||
<linearGradient
|
||||
id="linearGradient3593">
|
||||
<stop
|
||||
style="stop-color:#c8e0f9;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop3595" />
|
||||
<stop
|
||||
style="stop-color:#637dca;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3597" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient3593"
|
||||
id="radialGradient3354"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
cx="330.63791"
|
||||
cy="39.962704"
|
||||
fx="330.63791"
|
||||
fy="39.962704"
|
||||
r="19.571428"
|
||||
gradientTransform="translate(-0.1767767,-2.6516504)" />
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient3864"
|
||||
id="radialGradient3369"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.6258409,0.5434973,-8.8819886e-2,0.2656996,-461.81066,-173.06271)"
|
||||
cx="342.58258"
|
||||
cy="27.256668"
|
||||
fx="342.58258"
|
||||
fy="27.256668"
|
||||
r="19.571428" />
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient3593"
|
||||
id="radialGradient3372"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.0012324,0,0,0.9421773,-327.50313,-4.3316646)"
|
||||
cx="345.28433"
|
||||
cy="15.560534"
|
||||
fx="345.28433"
|
||||
fy="15.560534"
|
||||
r="19.571428" />
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient3593"
|
||||
id="radialGradient3375"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.0012324,0,0,0.9421773,-287.81791,-28.143054)"
|
||||
cx="330.63791"
|
||||
cy="39.962704"
|
||||
fx="330.63791"
|
||||
fy="39.962704"
|
||||
r="19.571428" />
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient3864"
|
||||
id="radialGradient3380"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.9829174,1.3240854,-1.2330051,0.8105158,-131.04134,-483.74563)"
|
||||
cx="320.44025"
|
||||
cy="113.23357"
|
||||
fx="320.44025"
|
||||
fy="113.23357"
|
||||
r="19.571428" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient3864"
|
||||
id="linearGradient3914"
|
||||
x1="6.94525"
|
||||
y1="36.838673"
|
||||
x2="48.691113"
|
||||
y2="36.838673"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.0012324,0,0,0.9421773,-4.8699606,-2.3863162)" />
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient3794"
|
||||
id="radialGradient3800"
|
||||
cx="1"
|
||||
cy="45"
|
||||
fx="1"
|
||||
fy="45"
|
||||
r="41"
|
||||
gradientTransform="matrix(0.93348213,-2.2905276e-8,0,0.28687573,0.06651751,32.090592)"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
id="linearGradient3794">
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop3796" />
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:0"
|
||||
offset="1"
|
||||
id="stop3798" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
gradientTransform="translate(0,-9)"
|
||||
xlink:href="#linearGradient3777"
|
||||
id="linearGradient3783"
|
||||
x1="53.896763"
|
||||
y1="51.179787"
|
||||
x2="48"
|
||||
y2="32"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
id="linearGradient3777">
|
||||
<stop
|
||||
style="stop-color:#204a87;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop3779" />
|
||||
<stop
|
||||
style="stop-color:#3465a4;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3781" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
gradientTransform="translate(22,-17)"
|
||||
xlink:href="#linearGradient3767"
|
||||
id="linearGradient3773"
|
||||
x1="22.116516"
|
||||
y1="55.717518"
|
||||
x2="19"
|
||||
y2="33"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
id="linearGradient3767">
|
||||
<stop
|
||||
style="stop-color:#3465a4;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop3769" />
|
||||
<stop
|
||||
style="stop-color:#729fcf;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3771" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
gradientTransform="translate(-2,-11)"
|
||||
xlink:href="#linearGradient3777-6"
|
||||
id="linearGradient3783-3"
|
||||
x1="53.896763"
|
||||
y1="51.179787"
|
||||
x2="48"
|
||||
y2="32"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
id="linearGradient3777-6">
|
||||
<stop
|
||||
style="stop-color:#204a87;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop3779-7" />
|
||||
<stop
|
||||
style="stop-color:#3465a4;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3781-5" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
y2="32"
|
||||
x2="48"
|
||||
y1="51.179787"
|
||||
x1="53.896763"
|
||||
gradientTransform="translate(-24,-13)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3066"
|
||||
xlink:href="#linearGradient3777-6" />
|
||||
<linearGradient
|
||||
y2="5"
|
||||
x2="-22"
|
||||
y1="18"
|
||||
x1="-18"
|
||||
gradientTransform="matrix(0.53559025,0,0,0.53556875,-6.4812797,2.8041108)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3323-3"
|
||||
xlink:href="#linearGradient3836-9-3-7-2-7-7-2-13" />
|
||||
<linearGradient
|
||||
id="linearGradient3836-9-3-7-2-7-7-2-13">
|
||||
<stop
|
||||
style="stop-color:#a40000;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop3838-8-5-4-4-6-4-4-8" />
|
||||
<stop
|
||||
style="stop-color:#ef2929;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3840-1-6-0-5-1-0-5-9" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3836-9-3-7-2-7-7-2-13"
|
||||
id="linearGradient1092"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.53559025,0,0,0.53556875,-6.4812797,2.8041108)"
|
||||
x1="-18"
|
||||
y1="18"
|
||||
x2="-22"
|
||||
y2="5" />
|
||||
</defs>
|
||||
<metadata
|
||||
id="metadata3369">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>[wmayer]</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
<dc:date>2011-10-10</dc:date>
|
||||
<dc:relation>http://www.freecadweb.org/wiki/index.php?title=Artwork</dc:relation>
|
||||
<dc:publisher>
|
||||
<cc:Agent>
|
||||
<dc:title>FreeCAD</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:publisher>
|
||||
<dc:identifier>FreeCAD/src/Mod/Part/Gui/Resources/icons/Part_Section.svg</dc:identifier>
|
||||
<dc:rights>
|
||||
<cc:Agent>
|
||||
<dc:title>FreeCAD LGPL2+</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:rights>
|
||||
<cc:license>https://www.gnu.org/copyleft/lesser.html</cc:license>
|
||||
<dc:contributor>
|
||||
<cc:Agent>
|
||||
<dc:title>[agryson] Alexander Gryson</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:contributor>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<path
|
||||
style="fill:none;stroke:#363636;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:6, 3;stroke-dashoffset:0;stroke-opacity:1"
|
||||
d="M 2.3798139,32.218121 H 61.620186"
|
||||
id="path985-9" />
|
||||
<path
|
||||
style="fill:none;stroke:#363636;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:6, 3;stroke-dashoffset:0;stroke-opacity:1"
|
||||
d="M 2.2324867,51.127858 H 61.472858"
|
||||
id="path985-6" />
|
||||
<path
|
||||
style="fill:none;stroke:#363636;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:6, 3;stroke-dashoffset:0;stroke-opacity:1"
|
||||
d="M 12.221742,61.620185 V 2.3798148"
|
||||
id="path985-92" />
|
||||
<path
|
||||
style="fill:none;stroke:#363636;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:6, 3;stroke-dashoffset:0;stroke-opacity:1"
|
||||
d="M 31.131479,61.620186 V 2.3798143"
|
||||
id="path985-9-8" />
|
||||
<path
|
||||
style="fill:none;stroke:#363636;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:6, 3;stroke-dashoffset:0;stroke-opacity:1"
|
||||
d="M 50.041216,61.620185 V 2.3798148"
|
||||
id="path985-6-2" />
|
||||
<path
|
||||
style="fill:none;stroke:#363636;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:6, 3;stroke-dashoffset:0;stroke-opacity:1"
|
||||
d="M 2.2324859,13.308385 H 61.472857"
|
||||
id="path985" />
|
||||
<g
|
||||
id="g4273"
|
||||
transform="translate(-85.253531,6.1052318)">
|
||||
<path
|
||||
style="fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 108.96148,19.97344 c 4.28933,11.161025 8.9416,20.384136 14.87384,32.552457 l 5.94954,-11.134131 12.749,12.154051 c 3.39402,0.638464 5.31688,-1.489816 4.75963,-4.504649 l -13.00398,-11.814076 8.49933,-6.629483 z"
|
||||
id="path1052"
|
||||
sodipodi:nodetypes="cccccccc" />
|
||||
<path
|
||||
style="fill:#d3d7cf;fill-opacity:1;stroke:#ffffff;stroke-width:1.285;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 112.59215,23.233162 c 3.72959,10.062766 6.25342,13.924184 11.41153,24.895126 l 5.26419,-9.586262 13.8638,13.109482 c 1.42608,0.530564 2.84842,-0.286358 2.07091,-2.088013 l -13.80942,-12.277572 7.29489,-5.92068 z"
|
||||
id="path1052-8"
|
||||
sodipodi:nodetypes="cccccccc" />
|
||||
</g>
|
||||
<g
|
||||
transform="rotate(63.436651,45.794909,-8.4224816)"
|
||||
id="g3529-4">
|
||||
<path
|
||||
id="path3491-6"
|
||||
d="M 56,8 V 32"
|
||||
style="fill:none;stroke:#2e3436;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
<path
|
||||
id="path3491-4-07"
|
||||
d="M 56,8 V 32"
|
||||
style="fill:none;stroke:#d3d7cf;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
<path
|
||||
id="path3491-4-1-17"
|
||||
d="M 55,8 V 32"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
</g>
|
||||
<g
|
||||
style="stroke-width:6.44377;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
transform="matrix(0.35785226,0.71575769,-0.71574725,0.35784704,48.084512,16.896405)"
|
||||
id="g3797-7-2-9-5-4-9-5-4">
|
||||
<path
|
||||
style="fill:#ef2929;stroke:#280000;stroke-width:2.4993;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4250-6-9-4-1-2-4-1-4"
|
||||
d="m -21.570202,4.8706993 a 6.2460052,6.2456584 0.01677682 1 1 9.488239,8.1250407 6.2460052,6.2456584 0.01677682 1 1 -9.488239,-8.1250407 z" />
|
||||
<path
|
||||
style="fill:url(#linearGradient1092);fill-opacity:1;stroke:#ef2929;stroke-width:2.4993;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4250-7-0-1-8-7-3-8-7-6"
|
||||
d="m -19.674789,6.4961011 a 3.7491316,3.7489813 0 1 1 5.695246,4.8771099 3.7491316,3.7489813 0 0 1 -5.695246,-4.8771099 z" />
|
||||
</g>
|
||||
<g
|
||||
style="stroke-width:6.44377;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
transform="matrix(0.35785226,0.71575769,-0.71574725,0.35784704,26.61794,27.628894)"
|
||||
id="g3797-7-2-9-5-4-9-5-4-0">
|
||||
<path
|
||||
style="fill:#ef2929;stroke:#280000;stroke-width:2.4993;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4250-6-9-4-1-2-4-1-4-1"
|
||||
d="m -21.570202,4.8706993 a 6.2460052,6.2456584 0.01677682 1 1 9.488239,8.1250407 6.2460052,6.2456584 0.01677682 1 1 -9.488239,-8.1250407 z" />
|
||||
<path
|
||||
style="fill:url(#linearGradient3323-3);fill-opacity:1;stroke:#ef2929;stroke-width:2.4993;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4250-7-0-1-8-7-3-8-7-6-6"
|
||||
d="m -19.674789,6.4961011 a 3.7491316,3.7489813 0 1 1 5.695246,4.8771099 3.7491316,3.7489813 0 0 1 -5.695246,-4.8771099 z" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 14 KiB |
@@ -0,0 +1,370 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2023 Pierre-Louis Boyer <pierrelouis.boyer@gmail.com> *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "PreCompiled.h"
|
||||
#ifndef _PreComp_
|
||||
#include <QApplication>
|
||||
#endif // #ifndef _PreComp_
|
||||
|
||||
#include <Mod/Sketcher/App/SketchObject.h>
|
||||
|
||||
#include "SnapManager.h"
|
||||
#include "ViewProviderSketch.h"
|
||||
|
||||
|
||||
using namespace SketcherGui;
|
||||
using namespace Sketcher;
|
||||
|
||||
/************************************ Attorney *******************************************/
|
||||
|
||||
inline int ViewProviderSketchSnapAttorney::getPreselectPoint(const ViewProviderSketch& vp)
|
||||
{
|
||||
return vp.getPreselectPoint();
|
||||
}
|
||||
|
||||
inline int ViewProviderSketchSnapAttorney::getPreselectCross(const ViewProviderSketch& vp)
|
||||
{
|
||||
return vp.getPreselectCross();
|
||||
}
|
||||
|
||||
inline int ViewProviderSketchSnapAttorney::getPreselectCurve(const ViewProviderSketch& vp)
|
||||
{
|
||||
return vp.getPreselectCurve();
|
||||
}
|
||||
|
||||
/**************************** ParameterObserver nested class *****************************/
|
||||
SnapManager::ParameterObserver::ParameterObserver(SnapManager& client) : client(client)
|
||||
{
|
||||
initParameters();
|
||||
subscribeToParameters();
|
||||
}
|
||||
|
||||
SnapManager::ParameterObserver::~ParameterObserver()
|
||||
{
|
||||
unsubscribeToParameters();
|
||||
}
|
||||
|
||||
void SnapManager::ParameterObserver::initParameters()
|
||||
{
|
||||
// static map to avoid substantial if/else branching
|
||||
//
|
||||
// key->first => String of parameter,
|
||||
// key->second => Update function to be called for the parameter,
|
||||
str2updatefunction = {
|
||||
{"Snap",
|
||||
[this](const std::string& param) {updateSnapParameter(param); }},
|
||||
{"SnapToObjects",
|
||||
[this](const std::string& param) {updateSnapToObjectParameter(param); }},
|
||||
{"SnapToGrid",
|
||||
[this](const std::string& param) {updateSnapToGridParameter(param); }},
|
||||
{"SnapAngle",
|
||||
[this](const std::string& param) {updateSnapAngleParameter(param); }},
|
||||
};
|
||||
|
||||
for (auto& val : str2updatefunction) {
|
||||
auto string = val.first;
|
||||
auto function = val.second;
|
||||
|
||||
function(string);
|
||||
}
|
||||
}
|
||||
|
||||
void SnapManager::ParameterObserver::updateSnapParameter(const std::string& parametername)
|
||||
{
|
||||
ParameterGrp::handle hGrp = getParameterGrpHandle();
|
||||
|
||||
client.snapRequested = hGrp->GetBool(parametername.c_str(), true);
|
||||
}
|
||||
|
||||
void SnapManager::ParameterObserver::updateSnapToObjectParameter(const std::string& parametername)
|
||||
{
|
||||
ParameterGrp::handle hGrp = getParameterGrpHandle();
|
||||
|
||||
client.snapToObjectsRequested = hGrp->GetBool(parametername.c_str(), true);
|
||||
}
|
||||
|
||||
void SnapManager::ParameterObserver::updateSnapToGridParameter(const std::string& parametername)
|
||||
{
|
||||
ParameterGrp::handle hGrp = getParameterGrpHandle();
|
||||
|
||||
client.snapToGridRequested = hGrp->GetBool(parametername.c_str(), false);
|
||||
}
|
||||
|
||||
void SnapManager::ParameterObserver::updateSnapAngleParameter(const std::string& parametername)
|
||||
{
|
||||
ParameterGrp::handle hGrp = getParameterGrpHandle();
|
||||
|
||||
client.snapAngle = fmod(hGrp->GetFloat(parametername.c_str(), 5.) * M_PI / 180, 2 * M_PI);
|
||||
}
|
||||
|
||||
void SnapManager::ParameterObserver::subscribeToParameters()
|
||||
{
|
||||
try {
|
||||
ParameterGrp::handle hGrp = getParameterGrpHandle();
|
||||
hGrp->Attach(this);
|
||||
}
|
||||
catch (const Base::ValueError& e) { // ensure that if parameter strings are not well-formed, the exception is not propagated
|
||||
Base::Console().Error("SnapManager: Malformed parameter string: %s\n", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void SnapManager::ParameterObserver::unsubscribeToParameters()
|
||||
{
|
||||
try {
|
||||
ParameterGrp::handle hGrp = getParameterGrpHandle();
|
||||
hGrp->Detach(this);
|
||||
}
|
||||
catch (const Base::ValueError& e) {// ensure that if parameter strings are not well-formed, the program is not terminated when calling the noexcept destructor.
|
||||
Base::Console().Error("SnapManager: Malformed parameter string: %s\n", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void SnapManager::ParameterObserver::OnChange(Base::Subject<const char*>& rCaller, const char* sReason)
|
||||
{
|
||||
(void)rCaller;
|
||||
|
||||
auto key = str2updatefunction.find(sReason);
|
||||
if (key != str2updatefunction.end()) {
|
||||
auto string = key->first;
|
||||
auto function = key->second;
|
||||
|
||||
function(string);
|
||||
}
|
||||
}
|
||||
|
||||
ParameterGrp::handle SnapManager::ParameterObserver::getParameterGrpHandle()
|
||||
{
|
||||
return App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher/Snap");
|
||||
}
|
||||
|
||||
//**************************** SnapManager class ******************************
|
||||
|
||||
SnapManager::SnapManager(ViewProviderSketch &vp):viewProvider(vp), angleSnapRequested(false), referencePoint(Base::Vector2d(0.,0.)), lastMouseAngle(0.0)
|
||||
{
|
||||
// Create parameter observer and initialise watched parameters
|
||||
pObserver = std::make_unique<SnapManager::ParameterObserver>(*this);
|
||||
}
|
||||
|
||||
SnapManager::~SnapManager() {}
|
||||
|
||||
bool SnapManager::snap(double& x, double& y)
|
||||
{
|
||||
if (!snapRequested)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//In order of priority :
|
||||
|
||||
// 1 - Snap at an angle
|
||||
if (angleSnapRequested && QApplication::keyboardModifiers() == Qt::ControlModifier) {
|
||||
return snapAtAngle(x, y);
|
||||
}
|
||||
else {
|
||||
lastMouseAngle = 0.0;
|
||||
}
|
||||
|
||||
// 2 - Snap to objects
|
||||
if (snapToObjectsRequested
|
||||
&& snapToObject(x, y)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 3 - Snap to grid
|
||||
if (snapToGridRequested /*&& viewProvider.ShowGrid.getValue() */ ) { //Snap to grid is enabled even if the grid is not visible.
|
||||
return snapToGrid(x, y);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SnapManager::snapAtAngle(double& x, double& y)
|
||||
{
|
||||
Base::Vector2d pointToOverride(x, y);
|
||||
double length = (pointToOverride - referencePoint).Length();
|
||||
|
||||
double angle1 = (pointToOverride - referencePoint).Angle();
|
||||
double angle2 = angle1 + (angle1 < 0. ? 2 : -2) * M_PI;
|
||||
lastMouseAngle = abs(angle1 - lastMouseAngle) < abs(angle2 - lastMouseAngle) ? angle1 : angle2;
|
||||
|
||||
double angle = round(lastMouseAngle / snapAngle) * snapAngle;
|
||||
pointToOverride = referencePoint + length * Base::Vector2d(cos(angle), sin(angle));
|
||||
x = pointToOverride.x;
|
||||
y = pointToOverride.y;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SnapManager::snapToObject(double& x, double& y)
|
||||
{
|
||||
Sketcher::SketchObject* Obj = viewProvider.getSketchObject();
|
||||
int geoId = GeoEnum::GeoUndef;
|
||||
Sketcher::PointPos posId = Sketcher::PointPos::none;
|
||||
|
||||
int VtId = ViewProviderSketchSnapAttorney::getPreselectPoint(viewProvider);
|
||||
int CrsId = ViewProviderSketchSnapAttorney::getPreselectCross(viewProvider);
|
||||
int CrvId = ViewProviderSketchSnapAttorney::getPreselectCurve(viewProvider);
|
||||
|
||||
if (CrsId == 0 || VtId >= 0) {
|
||||
if (CrsId == 0) {
|
||||
geoId = Sketcher::GeoEnum::RtPnt;
|
||||
posId = Sketcher::PointPos::start;
|
||||
}
|
||||
else if (VtId >= 0) {
|
||||
Obj->getGeoVertexIndex(VtId, geoId, posId);
|
||||
}
|
||||
|
||||
x = Obj->getPoint(geoId, posId).x;
|
||||
y = Obj->getPoint(geoId, posId).y;
|
||||
return true;
|
||||
}
|
||||
else if (CrsId == 1) { //H_Axis
|
||||
y = 0;
|
||||
return true;
|
||||
}
|
||||
else if (CrsId == 2) { //V_Axis
|
||||
x = 0;
|
||||
return true;
|
||||
}
|
||||
else if (CrvId >= 0 || CrvId <= Sketcher::GeoEnum::RefExt) { //Curves
|
||||
|
||||
const Part::Geometry* geo = Obj->getGeometry(CrvId);
|
||||
|
||||
Base::Vector3d pointToOverride(x, y, 0.);
|
||||
|
||||
double pointParam = 0.0;
|
||||
auto curve = dynamic_cast<const Part::GeomCurve*>(geo);
|
||||
if (curve) {
|
||||
try {
|
||||
curve->closestParameter(pointToOverride, pointParam);
|
||||
pointToOverride = curve->pointAtParameter(pointParam);
|
||||
}
|
||||
catch (Base::CADKernelError& e) {
|
||||
e.ReportException();
|
||||
return false;
|
||||
}
|
||||
|
||||
//If it is a line, then we check if we need to snap to the middle.
|
||||
if (geo->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
|
||||
const Part::GeomLineSegment* line = static_cast<const Part::GeomLineSegment*>(geo);
|
||||
snapToLineMiddle(pointToOverride, line);
|
||||
}
|
||||
|
||||
//If it is an arc, then we check if we need to snap to the middle (not the center).
|
||||
if (geo->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) {
|
||||
const Part::GeomArcOfCircle* arc = static_cast<const Part::GeomArcOfCircle*>(geo);
|
||||
snapToArcMiddle(pointToOverride, arc);
|
||||
}
|
||||
|
||||
x = pointToOverride.x;
|
||||
y = pointToOverride.y;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SnapManager::snapToGrid(double& x, double& y)
|
||||
{
|
||||
// Snap Tolerance in pixels
|
||||
const double snapTol = viewProvider.getGridSize() / 5;
|
||||
|
||||
double tmpX = x, tmpY = y;
|
||||
|
||||
viewProvider.getClosestGridPoint(tmpX, tmpY);
|
||||
|
||||
bool snapped = false;
|
||||
|
||||
// Check if x within snap tolerance
|
||||
if (x < tmpX + snapTol && x > tmpX - snapTol) {
|
||||
x = tmpX; // Snap X Mouse Position
|
||||
snapped = true;
|
||||
}
|
||||
|
||||
// Check if y within snap tolerance
|
||||
if (y < tmpY + snapTol && y > tmpY - snapTol) {
|
||||
y = tmpY; // Snap Y Mouse Position
|
||||
snapped = true;
|
||||
}
|
||||
|
||||
return snapped;
|
||||
}
|
||||
|
||||
bool SnapManager::snapToLineMiddle(Base::Vector3d& pointToOverride, const Part::GeomLineSegment* line)
|
||||
{
|
||||
Base::Vector3d startPoint = line->getStartPoint();
|
||||
Base::Vector3d endPoint = line->getEndPoint();
|
||||
Base::Vector3d midPoint = (startPoint + endPoint) / 2;
|
||||
|
||||
//Check if we are at middle of the line and if so snap to it.
|
||||
if ((pointToOverride - midPoint).Length() < (endPoint - startPoint).Length() * 0.05) {
|
||||
pointToOverride = midPoint;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SnapManager::snapToArcMiddle(Base::Vector3d& pointToOverride, const Part::GeomArcOfCircle* arc)
|
||||
{
|
||||
Base::Vector3d centerPoint = arc->getCenter();
|
||||
Base::Vector3d startVec = (arc->getStartPoint() - centerPoint);
|
||||
Base::Vector3d middleVec = startVec + (arc->getEndPoint() - centerPoint);
|
||||
|
||||
/* Handle the case of arc angle = 180 */
|
||||
if (middleVec.Length() < Precision::Confusion()) {
|
||||
middleVec.x = startVec.y;
|
||||
middleVec.y = -startVec.x;
|
||||
}
|
||||
else {
|
||||
middleVec = middleVec / middleVec.Length() * arc->getRadius();
|
||||
}
|
||||
|
||||
Base::Vector2d mVec = Base::Vector2d(middleVec.x, middleVec.y);
|
||||
Base::Vector3d pointVec = pointToOverride - centerPoint;
|
||||
Base::Vector2d pVec = Base::Vector2d(pointVec.x, pointVec.y);
|
||||
|
||||
double u, v;
|
||||
arc->getRange(u, v, true);
|
||||
if (v < u)
|
||||
v += 2 * M_PI;
|
||||
double angle = v - u;
|
||||
int revert = angle < M_PI ? 1 : -1;
|
||||
|
||||
/*To know if we are close to the middle of the arc, we are going to compare the angle of the
|
||||
* (mouse cursor - center) to the angle of the middle of the arc. If it's less than 10% of the arc angle, then we snap.
|
||||
*/
|
||||
if (fabs(pVec.Angle() - (revert * mVec).Angle()) < 0.10 * angle) {
|
||||
pointToOverride = centerPoint + middleVec * revert;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void SnapManager::setAngleSnapping(bool enable, Base::Vector2d referencepoint)
|
||||
{
|
||||
angleSnapRequested = enable;
|
||||
referencePoint = referencepoint;
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2023 Pierre-Louis Boyer <pierrelouis.boyer@gmail.com> *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
* SnapManager initially funded by the Open Toolchain Foundation *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef SKETCHERGUI_SnapManager_H
|
||||
#define SKETCHERGUI_SnapManager_H
|
||||
|
||||
|
||||
#include <App/Application.h>
|
||||
|
||||
|
||||
namespace SketcherGui {
|
||||
|
||||
class ViewProviderSketch;
|
||||
|
||||
|
||||
class ViewProviderSketchSnapAttorney {
|
||||
private:
|
||||
|
||||
static inline int getPreselectPoint(const ViewProviderSketch& vp);
|
||||
static inline int getPreselectCross(const ViewProviderSketch& vp);
|
||||
static inline int getPreselectCurve(const ViewProviderSketch& vp);
|
||||
|
||||
friend class SnapManager;
|
||||
};
|
||||
|
||||
/* This class is used to manage the overriding of mouse pointer coordinates in Sketcher
|
||||
* (in Edit-Mode) depending on the situation. Those situations are in priority order :
|
||||
* 1 - Snap at angle: For tools like Slot, Arc, Line, Ellipse, this enables to constrain the angle at steps of 5° (or customized angle).
|
||||
* This is useful to make features at a certain angle (45° for example)
|
||||
* 2 - Snap to object: This snaps the mouse pointer onto objects.
|
||||
* 3 - Snap to grid: This snaps the mouse pointer on the grid.
|
||||
*/
|
||||
class SnapManager
|
||||
{
|
||||
|
||||
/** @brief Class for monitoring changes in parameters affecting Snapping
|
||||
* @details
|
||||
*
|
||||
* This nested class is a helper responsible for attaching to the parameters relevant for
|
||||
* SnapManager, initialising the SnapManager to the current configuration
|
||||
* and handle in real time any change to their values.
|
||||
*/
|
||||
class ParameterObserver : public ParameterGrp::ObserverType
|
||||
{
|
||||
public:
|
||||
explicit ParameterObserver(SnapManager& client);
|
||||
~ParameterObserver() override;
|
||||
|
||||
void subscribeToParameters();
|
||||
|
||||
void unsubscribeToParameters();
|
||||
|
||||
/** Observer for parameter group. */
|
||||
void OnChange(Base::Subject<const char*>& rCaller, const char* sReason) override;
|
||||
|
||||
private:
|
||||
void initParameters();
|
||||
void updateSnapParameter(const std::string& parametername);
|
||||
void updateSnapToObjectParameter(const std::string& parametername);
|
||||
void updateSnapToGridParameter(const std::string& parametername);
|
||||
void updateSnapAngleParameter(const std::string& parametername);
|
||||
|
||||
static ParameterGrp::handle getParameterGrpHandle();
|
||||
|
||||
private:
|
||||
std::map<std::string, std::function<void(const std::string&)>> str2updatefunction;
|
||||
SnapManager& client;
|
||||
};
|
||||
|
||||
public:
|
||||
explicit SnapManager(ViewProviderSketch &vp);
|
||||
~SnapManager();
|
||||
|
||||
bool snap(double& x, double& y);
|
||||
bool snapAtAngle(double& x, double& y);
|
||||
bool snapToObject(double& x, double& y);
|
||||
bool snapToGrid(double& x, double& y);
|
||||
|
||||
bool snapToLineMiddle(Base::Vector3d& pointToOverride, const Part::GeomLineSegment* line);
|
||||
bool snapToArcMiddle(Base::Vector3d& pointToOverride, const Part::GeomArcOfCircle* arc);
|
||||
|
||||
void setAngleSnapping(bool enable, Base::Vector2d referencepoint);
|
||||
|
||||
private:
|
||||
/// Reference to ViewProviderSketch in order to access the public and the Attorney Interface
|
||||
ViewProviderSketch & viewProvider;
|
||||
|
||||
bool angleSnapRequested;
|
||||
bool snapRequested;
|
||||
bool snapToObjectsRequested;
|
||||
bool snapToGridRequested;
|
||||
|
||||
Base::Vector2d referencePoint;
|
||||
double lastMouseAngle;
|
||||
|
||||
double snapAngle;
|
||||
|
||||
/// Observer to track all the needed parameters.
|
||||
std::unique_ptr<SnapManager::ParameterObserver> pObserver;
|
||||
};
|
||||
|
||||
|
||||
} // namespace SketcherGui
|
||||
|
||||
|
||||
#endif // SKETCHERGUI_SnapManager_H
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="showHideButton">
|
||||
<widget class="QToolButton" name="showHideButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
#include <Gui/Application.h>
|
||||
#include <Gui/BitmapFactory.h>
|
||||
#include <Gui/Command.h>
|
||||
#include <Gui/Notifications.h>
|
||||
#include <Gui/Selection.h>
|
||||
#include <Gui/SelectionObject.h>
|
||||
#include <Gui/ViewProvider.h>
|
||||
@@ -74,6 +75,29 @@ void ElementView::FUNC(){ \
|
||||
|
||||
|
||||
namespace SketcherGui {
|
||||
|
||||
class ElementItemDelegate : public QStyledItemDelegate
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ElementItemDelegate(ElementView* parent);
|
||||
~ElementItemDelegate() override;
|
||||
|
||||
void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
|
||||
bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index) override;
|
||||
|
||||
ElementItem* getElementtItem(const QModelIndex& index) const;
|
||||
|
||||
const int border = 1; //1px, looks good around buttons.
|
||||
const int leftMargin = 4; //4px on the left of icons, looks good.
|
||||
mutable int customIconsMargin = 4;
|
||||
const int textBottomMargin = 5; //5px center the text.
|
||||
|
||||
Q_SIGNALS:
|
||||
void itemHovered(QModelIndex);
|
||||
void itemChecked(QModelIndex, Qt::CheckState state);
|
||||
};
|
||||
|
||||
// helper class to store additional information about the listWidget entry.
|
||||
class ElementItem : public QListWidgetItem
|
||||
{
|
||||
@@ -93,7 +117,7 @@ class ElementItem : public QListWidgetItem
|
||||
};
|
||||
|
||||
ElementItem(int elementnr, int startingVertex, int midVertex, int endVertex,
|
||||
Base::Type geometryType, GeometryState state, const QString & lab, const Part::Geometry * geo) :
|
||||
Base::Type geometryType, GeometryState state, const QString & lab, ViewProviderSketch *sketchView) :
|
||||
ElementNbr(elementnr)
|
||||
, StartingVertex(startingVertex)
|
||||
, MidVertex(midVertex)
|
||||
@@ -108,7 +132,7 @@ class ElementItem : public QListWidgetItem
|
||||
, hovered(SubElementType::none)
|
||||
, rightClicked(false)
|
||||
, label(lab)
|
||||
, geo(geo)
|
||||
, sketchView(sketchView)
|
||||
{
|
||||
|
||||
}
|
||||
@@ -117,9 +141,20 @@ class ElementItem : public QListWidgetItem
|
||||
}
|
||||
|
||||
bool isVisible() {
|
||||
auto layer = getSafeGeomLayerId(geo);
|
||||
|
||||
return layer != static_cast<unsigned int>(Layer::Hidden);
|
||||
if(State != GeometryState::External) {
|
||||
const auto geo = sketchView->getSketchObject()->getGeometry(ElementNbr);
|
||||
if(geo) {
|
||||
auto layer = getSafeGeomLayerId(geo);
|
||||
|
||||
return layer != static_cast<unsigned int>(Layer::Hidden);
|
||||
}
|
||||
}
|
||||
|
||||
// 1. external geometry currently is always visible.
|
||||
// 2. if internal and ElementNbr is out of range, the element
|
||||
// needs to be updated and the return value is not important.
|
||||
return true;
|
||||
}
|
||||
|
||||
int ElementNbr;
|
||||
@@ -142,7 +177,41 @@ class ElementItem : public QListWidgetItem
|
||||
|
||||
QString label;
|
||||
|
||||
const Part::Geometry * geo;
|
||||
private:
|
||||
ViewProviderSketch *sketchView;
|
||||
};
|
||||
|
||||
class ElementFilterList : public QListWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ElementFilterList(QWidget* parent = nullptr);
|
||||
~ElementFilterList() override;
|
||||
|
||||
protected:
|
||||
void changeEvent(QEvent* e) override;
|
||||
virtual void languageChange();
|
||||
|
||||
private:
|
||||
using filterItemRepr = std::pair<const char *, const int>; // {filter item text, filter item level}
|
||||
inline static const std::vector<filterItemRepr> filterItems = {
|
||||
{QT_TR_NOOP("Normal"),0},
|
||||
{QT_TR_NOOP("Construction"),0},
|
||||
{QT_TR_NOOP("Internal"),0},
|
||||
{QT_TR_NOOP("External"),0},
|
||||
{QT_TR_NOOP("All types"),0},
|
||||
{QT_TR_NOOP("Point"),1},
|
||||
{QT_TR_NOOP("Line"),1},
|
||||
{QT_TR_NOOP("Circle"),1},
|
||||
{QT_TR_NOOP("Ellipse"),1},
|
||||
{QT_TR_NOOP("Arc of circle"),1},
|
||||
{QT_TR_NOOP("Arc of ellipse"),1},
|
||||
{QT_TR_NOOP("Arc of hyperbola"),1},
|
||||
{QT_TR_NOOP("Arc of parabola"),1},
|
||||
{QT_TR_NOOP("B-Spline"),1}
|
||||
};
|
||||
|
||||
};
|
||||
} // SketcherGui
|
||||
|
||||
@@ -363,12 +432,19 @@ void ElementView::changeLayer(int layer)
|
||||
|
||||
bool anychanged = false;
|
||||
for(auto geoid : geoids) {
|
||||
auto currentlayer = getSafeGeomLayerId(geometry[geoid]);
|
||||
if( currentlayer != layer) {
|
||||
auto geo = geometry[geoid]->clone();
|
||||
setSafeGeomLayerId(geo, layer);
|
||||
newgeometry[geoid] = geo;
|
||||
anychanged = true;
|
||||
if(geoid >= 0) { // currently only internal geometry can be changed from one layer to another
|
||||
auto currentlayer = getSafeGeomLayerId(geometry[geoid]);
|
||||
if( currentlayer != layer) {
|
||||
auto geo = geometry[geoid]->clone();
|
||||
setSafeGeomLayerId(geo, layer);
|
||||
newgeometry[geoid] = geo;
|
||||
anychanged = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
Gui::TranslatedNotification(sketchobject,
|
||||
QObject::tr("Unsupported visual layer operation"),
|
||||
QObject::tr("It is currently unsupported to move external geometry to another visual layer. External geometry will be omitted"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -644,6 +720,7 @@ ElementItem* ElementItemDelegate::getElementtItem(const QModelIndex& index) cons
|
||||
}
|
||||
|
||||
/* Filter element list widget ------------------------------------------------------ */
|
||||
|
||||
enum class GeoFilterType {
|
||||
NormalGeos,
|
||||
ConstructionGeos,
|
||||
@@ -1304,8 +1381,8 @@ void TaskSketcherElements::slotElementsChanged(void)
|
||||
(isNamingBoxChecked ?
|
||||
(tr("Other") + IdInformation()) +
|
||||
(construction ? (QString::fromLatin1("-") + tr("Construction")) : (internalAligned ? (QString::fromLatin1("-") + tr("Internal")) : QString::fromLatin1(""))) :
|
||||
(QString::fromLatin1("%1-").arg(i) + tr("Other")))
|
||||
, (*it) // geometry
|
||||
(QString::fromLatin1("%1-").arg(i) + tr("Other"))),
|
||||
sketchView
|
||||
);
|
||||
|
||||
ui->listWidgetElements->addItem(itemN);
|
||||
@@ -1396,8 +1473,8 @@ void TaskSketcherElements::slotElementsChanged(void)
|
||||
(QString::fromLatin1("%1-").arg(i - 2) + tr("BSpline"))) :
|
||||
(isNamingBoxChecked ?
|
||||
(tr("Other") + linkname) :
|
||||
(QString::fromLatin1("%1-").arg(i - 2) + tr("Other")))
|
||||
, (*it) // geometry
|
||||
(QString::fromLatin1("%1-").arg(i - 2) + tr("Other"))),
|
||||
sketchView
|
||||
);
|
||||
|
||||
ui->listWidgetElements->addItem(itemN);
|
||||
@@ -1465,3 +1542,4 @@ void TaskSketcherElements::onSettingsExtendedInformationChanged()
|
||||
}
|
||||
|
||||
#include "moc_TaskSketcherElements.cpp"
|
||||
#include "TaskSketcherElements.moc" // For Delegate as it is QOBJECT
|
||||
|
||||
@@ -55,28 +55,6 @@ enum class SubElementType {
|
||||
none
|
||||
};
|
||||
|
||||
class ElementItemDelegate : public QStyledItemDelegate
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ElementItemDelegate(ElementView* parent);
|
||||
~ElementItemDelegate() override;
|
||||
|
||||
void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
|
||||
bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index) override;
|
||||
|
||||
ElementItem* getElementtItem(const QModelIndex& index) const;
|
||||
|
||||
const int border = 1; //1px, looks good around buttons.
|
||||
const int leftMargin = 4; //4px on the left of icons, looks good.
|
||||
mutable int customIconsMargin = 4;
|
||||
const int textBottomMargin = 5; //5px center the text.
|
||||
|
||||
Q_SIGNALS:
|
||||
void itemHovered(QModelIndex);
|
||||
void itemChecked(QModelIndex, Qt::CheckState state);
|
||||
};
|
||||
|
||||
class ElementView : public QListWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -131,38 +109,7 @@ private:
|
||||
void changeLayer(int layer);
|
||||
};
|
||||
|
||||
class ElementFilterList : public QListWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ElementFilterList(QWidget* parent = nullptr);
|
||||
~ElementFilterList() override;
|
||||
|
||||
protected:
|
||||
void changeEvent(QEvent* e) override;
|
||||
virtual void languageChange();
|
||||
|
||||
private:
|
||||
using filterItemRepr = std::pair<const char *, const int>; // {filter item text, filter item level}
|
||||
inline static const std::vector<filterItemRepr> filterItems = {
|
||||
{QT_TR_NOOP("Normal"),0},
|
||||
{QT_TR_NOOP("Construction"),0},
|
||||
{QT_TR_NOOP("Internal"),0},
|
||||
{QT_TR_NOOP("External"),0},
|
||||
{QT_TR_NOOP("All types"),0},
|
||||
{QT_TR_NOOP("Point"),1},
|
||||
{QT_TR_NOOP("Line"),1},
|
||||
{QT_TR_NOOP("Circle"),1},
|
||||
{QT_TR_NOOP("Ellipse"),1},
|
||||
{QT_TR_NOOP("Arc of circle"),1},
|
||||
{QT_TR_NOOP("Arc of ellipse"),1},
|
||||
{QT_TR_NOOP("Arc of hyperbola"),1},
|
||||
{QT_TR_NOOP("Arc of parabola"),1},
|
||||
{QT_TR_NOOP("B-Spline"),1}
|
||||
};
|
||||
|
||||
};
|
||||
class ElementFilterList;
|
||||
|
||||
class TaskSketcherElements : public Gui::TaskView::TaskBox, public Gui::SelectionObserver
|
||||
{
|
||||
|
||||
@@ -327,6 +327,39 @@ double SketcherGui::GetPointAngle(const Base::Vector2d& p1, const Base::Vector2d
|
||||
return dY >= 0 ? atan2(dY, dX) : atan2(dY, dX) + 2 * M_PI;
|
||||
}
|
||||
|
||||
// Set the two points on circles at minimal distance
|
||||
// in concentric case set points on relative X axis
|
||||
void SketcherGui::GetCirclesMinimalDistance(const Part::GeomCircle *circle1, const Part::GeomCircle *circle2, Base::Vector3d &point1, Base::Vector3d &point2)
|
||||
{
|
||||
double radius1 = circle1->getRadius();
|
||||
double radius2 = circle2->getRadius();
|
||||
|
||||
point1 = circle1->getCenter();
|
||||
point2 = circle2->getCenter();
|
||||
|
||||
Base::Vector3d v = point2 - point1;
|
||||
double length = v.Length();
|
||||
|
||||
if (length == 0) { //concentric case
|
||||
point1.x += radius1;
|
||||
point2.x += radius2;
|
||||
} else {
|
||||
v = v.Normalize();
|
||||
if (length <= std::max(radius1, radius2)){ //inner case
|
||||
if (radius1 > radius2){
|
||||
point1 += v * radius1;
|
||||
point2 += v * radius2;
|
||||
} else {
|
||||
point1 += -v * radius1;
|
||||
point2 += -v * radius2;
|
||||
}
|
||||
} else { //outer case
|
||||
point1 += v * radius1;
|
||||
point2 += -v * radius2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SketcherGui::ActivateHandler(Gui::Document* doc, DrawSketchHandler* handler)
|
||||
{
|
||||
std::unique_ptr<DrawSketchHandler> ptr(handler);
|
||||
|
||||
@@ -119,6 +119,9 @@ inline bool isEdge(int GeoId, Sketcher::PointPos PosId)
|
||||
// Return counter-clockwise angle from horizontal out of p1 to p2 in radians.
|
||||
double GetPointAngle (const Base::Vector2d &p1, const Base::Vector2d &p2);
|
||||
|
||||
// Set the two points on circles at minimal distance
|
||||
void GetCirclesMinimalDistance(const Part::GeomCircle *circle1, const Part::GeomCircle *circle2, Base::Vector3d &point1, Base::Vector3d &point2);
|
||||
|
||||
void ActivateHandler(Gui::Document *doc, DrawSketchHandler *handler);
|
||||
|
||||
/// Returns if a sketch is in edit mode
|
||||
|
||||
@@ -65,6 +65,7 @@
|
||||
#include "DrawSketchHandler.h"
|
||||
#include "EditDatumDialog.h"
|
||||
#include "EditModeCoinManager.h"
|
||||
#include "SnapManager.h"
|
||||
#include "TaskDlgEditSketch.h"
|
||||
#include "TaskSketcherValidation.h"
|
||||
#include "Utils.h"
|
||||
@@ -304,6 +305,7 @@ ViewProviderSketch::ViewProviderSketch()
|
||||
Mode(STATUS_NONE),
|
||||
listener(nullptr),
|
||||
editCoinManager(nullptr),
|
||||
snapManager(nullptr),
|
||||
pObserver(std::make_unique<ViewProviderSketch::ParameterObserver>(*this)),
|
||||
sketchHandler(nullptr),
|
||||
viewOrientationFactor(1)
|
||||
@@ -551,36 +553,10 @@ bool ViewProviderSketch::keyPressed(bool pressed, int key)
|
||||
return true; // handle all other key events
|
||||
}
|
||||
|
||||
void ViewProviderSketch::setSnapMode(SnapMode mode)
|
||||
void ViewProviderSketch::setAngleSnapping(bool enable, Base::Vector2d referencePoint)
|
||||
{
|
||||
snapMode = mode; // to be redirected to SnapManager
|
||||
}
|
||||
|
||||
SnapMode ViewProviderSketch::getSnapMode() const
|
||||
{
|
||||
return snapMode; // to be redirected to SnapManager
|
||||
}
|
||||
|
||||
void ViewProviderSketch::snapToGrid(double &x, double &y) // Paddle, when resolving this conflict, make sure to use the function in ViewProviderGridExtension
|
||||
{
|
||||
if (snapMode == SnapMode::SnapToGrid && ShowGrid.getValue()) {
|
||||
// Snap Tolerance in pixels
|
||||
const double snapTol = getGridSize() / 5;
|
||||
|
||||
double tmpX = x, tmpY = y;
|
||||
|
||||
getClosestGridPoint(tmpX, tmpY);
|
||||
|
||||
// Check if x within snap tolerance
|
||||
if (x < tmpX + snapTol && x > tmpX - snapTol) {
|
||||
x = tmpX; // Snap X Mouse Position
|
||||
}
|
||||
|
||||
// Check if y within snap tolerance
|
||||
if (y < tmpY + snapTol && y > tmpY - snapTol) {
|
||||
y = tmpY; // Snap Y Mouse Position
|
||||
}
|
||||
}
|
||||
assert(snapManager);
|
||||
snapManager->setAngleSnapping(enable, referencePoint);
|
||||
}
|
||||
|
||||
void ViewProviderSketch::getProjectingLine(const SbVec2s& pnt, const Gui::View3DInventorViewer *viewer, SbLine& line) const
|
||||
@@ -679,7 +655,7 @@ bool ViewProviderSketch::mouseButtonPressed(int Button, bool pressed, const SbVe
|
||||
|
||||
try {
|
||||
getCoordsOnSketchPlane(pos,normal,x,y);
|
||||
snapToGrid(x, y);
|
||||
snapManager->snap(x, y);
|
||||
}
|
||||
catch (const Base::ZeroDivisionError&) {
|
||||
return false;
|
||||
@@ -1148,7 +1124,7 @@ bool ViewProviderSketch::mouseMove(const SbVec2s &cursorPos, Gui::View3DInventor
|
||||
double x,y;
|
||||
try {
|
||||
getCoordsOnSketchPlane(line.getPosition(),line.getDirection(),x,y);
|
||||
snapToGrid(x, y);
|
||||
snapManager->snap(x, y);
|
||||
}
|
||||
catch (const Base::ZeroDivisionError&) {
|
||||
return false;
|
||||
@@ -1275,7 +1251,7 @@ bool ViewProviderSketch::mouseMove(const SbVec2s &cursorPos, Gui::View3DInventor
|
||||
SbLine line2;
|
||||
getProjectingLine(DoubleClick::prvCursorPos, viewer, line2);
|
||||
getCoordsOnSketchPlane(line2.getPosition(),line2.getDirection(),drag.xInit,drag.yInit);
|
||||
snapToGrid(drag.xInit, drag.yInit);
|
||||
snapManager->snap(drag.xInit, drag.yInit);
|
||||
} else {
|
||||
drag.resetVector();
|
||||
}
|
||||
@@ -1416,16 +1392,23 @@ void ViewProviderSketch::moveConstraint(int constNum, const Base::Vector2d &toPo
|
||||
if (Constr->SecondPos != Sketcher::PointPos::none) { // point to point distance
|
||||
p1 = getSolvedSketch().getPoint(Constr->First, Constr->FirstPos);
|
||||
p2 = getSolvedSketch().getPoint(Constr->Second, Constr->SecondPos);
|
||||
} else if (Constr->Second != GeoEnum::GeoUndef) { // point to line distance
|
||||
} else if (Constr->Second != GeoEnum::GeoUndef) {
|
||||
p1 = getSolvedSketch().getPoint(Constr->First, Constr->FirstPos);
|
||||
const Part::Geometry *geo = GeoList::getGeometryFromGeoId (geomlist, Constr->Second);
|
||||
if (geo->getTypeId() == Part::GeomLineSegment::getClassTypeId()) {
|
||||
if (geo->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { // point to line distance
|
||||
const Part::GeomLineSegment *lineSeg = static_cast<const Part::GeomLineSegment *>(geo);
|
||||
Base::Vector3d l2p1 = lineSeg->getStartPoint();
|
||||
Base::Vector3d l2p2 = lineSeg->getEndPoint();
|
||||
// calculate the projection of p1 onto line2
|
||||
p2.ProjectToLine(p1-l2p1, l2p2-l2p1);
|
||||
p2 += p1;
|
||||
} else if (geo->getTypeId() == Part::GeomCircle::getClassTypeId()) { // circle to circle distance
|
||||
const Part::Geometry *geo1 = GeoList::getGeometryFromGeoId (geomlist, Constr->First);
|
||||
if (geo1->getTypeId() == Part::GeomCircle::getClassTypeId()) {
|
||||
const Part::GeomCircle *circleSeg1 = static_cast<const Part::GeomCircle *>(geo1);
|
||||
const Part::GeomCircle *circleSeg2 = static_cast<const Part::GeomCircle *>(geo);
|
||||
GetCirclesMinimalDistance(circleSeg1, circleSeg2, p1, p2);
|
||||
}
|
||||
} else
|
||||
return;
|
||||
} else if (Constr->FirstPos != Sketcher::PointPos::none) {
|
||||
@@ -2870,6 +2853,7 @@ bool ViewProviderSketch::setEdit(int ModNum)
|
||||
preselection.reset();
|
||||
selection.reset();
|
||||
editCoinManager = std::make_unique<EditModeCoinManager>(*this);
|
||||
snapManager = std::make_unique<SnapManager>(*this);
|
||||
|
||||
auto editDoc = Gui::Application::Instance->editDocument();
|
||||
App::DocumentObject *editObj = getSketchObject();
|
||||
@@ -3116,6 +3100,7 @@ void ViewProviderSketch::unsetEdit(int ModNum)
|
||||
deactivateHandler();
|
||||
|
||||
editCoinManager = nullptr;
|
||||
snapManager = nullptr;
|
||||
preselection.reset();
|
||||
selection.reset();
|
||||
this->detachSelection();
|
||||
|
||||
@@ -85,15 +85,9 @@ namespace Sketcher {
|
||||
namespace SketcherGui {
|
||||
|
||||
class EditModeCoinManager;
|
||||
class SnapManager;
|
||||
class DrawSketchHandler;
|
||||
|
||||
enum class SnapMode { // to be moved to SnapManager
|
||||
None,
|
||||
SnapToObject,
|
||||
SnapToAngle,
|
||||
SnapToGrid,
|
||||
};
|
||||
|
||||
using GeoList = Sketcher::GeoList;
|
||||
using GeoListFacade = Sketcher::GeoListFacade;
|
||||
|
||||
@@ -484,8 +478,10 @@ public:
|
||||
void onSelectionChanged(const Gui::SelectionChanges& msg) override;
|
||||
//@}
|
||||
|
||||
void setSnapMode(SnapMode mode);
|
||||
SnapMode getSnapMode() const;
|
||||
/** @name Toggle angle snapping and set the reference point */
|
||||
//@{
|
||||
/// Toggle angle snapping and set the reference point
|
||||
void setAngleSnapping(bool enable, Base::Vector2d referencePoint = Base::Vector2d(0., 0.));
|
||||
|
||||
/** @name Access to Sketch and Solver objects */
|
||||
//@{
|
||||
@@ -566,6 +562,7 @@ public:
|
||||
//@{
|
||||
friend class ViewProviderSketchDrawSketchHandlerAttorney;
|
||||
friend class ViewProviderSketchCoinAttorney;
|
||||
friend class ViewProviderSketchSnapAttorney;
|
||||
friend class ViewProviderSketchShortcutListenerAttorney;
|
||||
//@}
|
||||
protected:
|
||||
@@ -660,9 +657,6 @@ private:
|
||||
|
||||
/** @name miscelanea utilities */
|
||||
//@{
|
||||
/// snap points x,y (mouse coordinates) onto grid if enabled
|
||||
void snapToGrid(double &x, double &y);
|
||||
|
||||
/// moves a selected constraint
|
||||
void moveConstraint(int constNum, const Base::Vector2d &toPos);
|
||||
|
||||
@@ -784,6 +778,8 @@ private:
|
||||
|
||||
std::unique_ptr<EditModeCoinManager> editCoinManager;
|
||||
|
||||
std::unique_ptr<SnapManager> snapManager;
|
||||
|
||||
std::unique_ptr<ViewProviderSketch::ParameterObserver> pObserver;
|
||||
|
||||
std::unique_ptr<DrawSketchHandler> sketchHandler;
|
||||
@@ -792,8 +788,6 @@ private:
|
||||
|
||||
SoNodeSensor cameraSensor;
|
||||
int viewOrientationFactor; // stores if sketch viewed from front or back
|
||||
|
||||
SnapMode snapMode = SnapMode::None; // temporary - to be moved to SnapManager
|
||||
};
|
||||
|
||||
} // namespace PartGui
|
||||
|
||||
@@ -188,7 +188,8 @@ inline void SketcherAddWorkbenchSketchEditModeActions(Gui::ToolBarItem& sketch)
|
||||
sketch << "Sketcher_LeaveSketch"
|
||||
<< "Sketcher_ViewSketch"
|
||||
<< "Sketcher_ViewSection"
|
||||
<< "Sketcher_Grid";
|
||||
<< "Sketcher_Grid"
|
||||
<< "Sketcher_Snap";
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
|
||||
@@ -175,6 +175,8 @@ SET(TechDraw_SRCS
|
||||
TechDrawExport.h
|
||||
ProjectionAlgos.cpp
|
||||
ProjectionAlgos.h
|
||||
XMLQuery.cpp
|
||||
XMLQuery.h
|
||||
)
|
||||
|
||||
SET(Geometry_SRCS
|
||||
|
||||
@@ -26,10 +26,7 @@
|
||||
#ifndef _PreComp_
|
||||
# include <sstream>
|
||||
# include <QDomDocument>
|
||||
# include <QDomNodeModel.h>
|
||||
# include <QFile>
|
||||
# include <QXmlQuery>
|
||||
# include <QXmlResultItems>
|
||||
#endif
|
||||
|
||||
#include <App/Application.h>
|
||||
@@ -44,6 +41,7 @@
|
||||
#include "DrawSVGTemplate.h"
|
||||
#include "DrawSVGTemplatePy.h"
|
||||
#include "DrawUtil.h"
|
||||
#include "XMLQuery.h"
|
||||
|
||||
|
||||
using namespace TechDraw;
|
||||
@@ -113,72 +111,64 @@ QString DrawSVGTemplate::processTemplate()
|
||||
return QString();
|
||||
}
|
||||
|
||||
QDomDocument templateDocument;
|
||||
if (!templateDocument.setContent(&templateFile)) {
|
||||
QDomDocument templateDocument;
|
||||
if (!templateDocument.setContent(&templateFile)) {
|
||||
Base::Console().Error("DrawSVGTemplate::processTemplate - failed to parse file: %s\n",
|
||||
PageResult.getValue());
|
||||
return QString();
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
QXmlQuery query(QXmlQuery::XQuery10);
|
||||
QDomNodeModel model(query.namePool(), templateDocument);
|
||||
query.setFocus(QXmlItem(model.fromDomNode(templateDocument.documentElement())));
|
||||
XMLQuery query(templateDocument);
|
||||
std::map<std::string, std::string> substitutions = EditableTexts.getValues();
|
||||
|
||||
// XPath query to select all <tspan> nodes whose <text> parent
|
||||
// has "freecad:editable" attribute
|
||||
query.setQuery(QString::fromUtf8(
|
||||
"declare default element namespace \"" SVG_NS_URI "\"; "
|
||||
"declare namespace freecad=\"" FREECAD_SVG_NS_URI "\"; "
|
||||
"//text[@freecad:editable]/tspan"));
|
||||
// XPath query to select all <tspan> nodes whose <text> parent
|
||||
// has "freecad:editable" attribute
|
||||
query.processItems(QString::fromUtf8(
|
||||
"declare default element namespace \"" SVG_NS_URI "\"; "
|
||||
"declare namespace freecad=\"" FREECAD_SVG_NS_URI "\"; "
|
||||
"//text[@freecad:editable]/tspan"),
|
||||
[&substitutions, &templateDocument](QDomElement& tspan) -> bool {
|
||||
// Replace the editable text spans with new nodes holding actual values
|
||||
QString editableName = tspan.parentNode().toElement().attribute(QString::fromUtf8("freecad:editable"));
|
||||
std::map<std::string, std::string>::iterator item =
|
||||
substitutions.find(editableName.toStdString());
|
||||
if (item != substitutions.end()) {
|
||||
// Keep all spaces in the text node
|
||||
tspan.setAttribute(QString::fromUtf8("xml:space"), QString::fromUtf8("preserve"));
|
||||
|
||||
QXmlResultItems queryResult;
|
||||
query.evaluateTo(&queryResult);
|
||||
// Remove all child nodes and append text node with editable replacement as the only descendant
|
||||
while (!tspan.lastChild().isNull()) {
|
||||
tspan.removeChild(tspan.lastChild());
|
||||
}
|
||||
tspan.appendChild(templateDocument.createTextNode(QString::fromUtf8(item->second.c_str())));
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
std::map<std::string, std::string> substitutions = EditableTexts.getValues();
|
||||
while (!queryResult.next().isNull())
|
||||
{
|
||||
QDomElement tspan = model.toDomNode(queryResult.current().toNodeModelIndex()).toElement();
|
||||
// Calculate the dimensions of the page and store for retrieval
|
||||
// Obtain the size of the SVG document by reading the document attributes
|
||||
QDomElement docElement = templateDocument.documentElement();
|
||||
Base::Quantity quantity;
|
||||
|
||||
// Replace the editable text spans with new nodes holding actual values
|
||||
QString editableName = tspan.parentNode().toElement().attribute(QString::fromUtf8("freecad:editable"));
|
||||
std::map<std::string, std::string>::iterator item =
|
||||
substitutions.find(std::string(editableName.toUtf8().constData()));
|
||||
if (item != substitutions.end()) {
|
||||
// Keep all spaces in the text node
|
||||
tspan.setAttribute(QString::fromUtf8("xml:space"), QString::fromUtf8("preserve"));
|
||||
// Obtain the width
|
||||
QString str = docElement.attribute(QString::fromLatin1("width"));
|
||||
quantity = Base::Quantity::parse(str);
|
||||
quantity.setUnit(Base::Unit::Length);
|
||||
|
||||
// Remove all child nodes and append text node with editable replacement as the only descendant
|
||||
while (!tspan.lastChild().isNull()) {
|
||||
tspan.removeChild(tspan.lastChild());
|
||||
}
|
||||
tspan.appendChild(templateDocument.createTextNode(QString::fromUtf8(item->second.c_str())));
|
||||
}
|
||||
}
|
||||
Width.setValue(quantity.getValue());
|
||||
|
||||
// Calculate the dimensions of the page and store for retrieval
|
||||
// Obtain the size of the SVG document by reading the document attributes
|
||||
QDomElement docElement = templateDocument.documentElement();
|
||||
Base::Quantity quantity;
|
||||
str = docElement.attribute(QString::fromLatin1("height"));
|
||||
quantity = Base::Quantity::parse(str);
|
||||
quantity.setUnit(Base::Unit::Length);
|
||||
|
||||
// Obtain the width
|
||||
QString str = docElement.attribute(QString::fromLatin1("width"));
|
||||
quantity = Base::Quantity::parse(str);
|
||||
quantity.setUnit(Base::Unit::Length);
|
||||
Height.setValue(quantity.getValue());
|
||||
|
||||
Width.setValue(quantity.getValue());
|
||||
bool isLandscape = getWidth() / getHeight() >= 1.;
|
||||
|
||||
str = docElement.attribute(QString::fromLatin1("height"));
|
||||
quantity = Base::Quantity::parse(str);
|
||||
quantity.setUnit(Base::Unit::Length);
|
||||
Orientation.setValue(isLandscape ? 1 : 0);
|
||||
|
||||
Height.setValue(quantity.getValue());
|
||||
|
||||
bool isLandscape = getWidth() / getHeight() >= 1.;
|
||||
|
||||
Orientation.setValue(isLandscape ? 1 : 0);
|
||||
|
||||
//all Qt holds on files should be released on exit #4085
|
||||
return templateDocument.toString();
|
||||
//all Qt holds on files should be released on exit #4085
|
||||
return templateDocument.toString();
|
||||
}
|
||||
|
||||
double DrawSVGTemplate::getWidth() const
|
||||
@@ -218,7 +208,7 @@ std::map<std::string, std::string> DrawSVGTemplate::getEditableTextsFromTemplate
|
||||
|
||||
Base::FileInfo tfi(templateFilename);
|
||||
if (!tfi.isReadable()) {
|
||||
// if there is a old absolute template file set use a redirect
|
||||
// if there is an old absolute template file set use a redirect
|
||||
tfi.setFile(App::Application::getResourceDir() + "Mod/Drawing/Templates/" + tfi.fileName());
|
||||
// try the redirect
|
||||
if (!tfi.isReadable()) {
|
||||
@@ -240,29 +230,22 @@ std::map<std::string, std::string> DrawSVGTemplate::getEditableTextsFromTemplate
|
||||
return editables;
|
||||
}
|
||||
|
||||
QXmlQuery query(QXmlQuery::XQuery10);
|
||||
QDomNodeModel model(query.namePool(), templateDocument, true);
|
||||
query.setFocus(QXmlItem(model.fromDomNode(templateDocument.documentElement())));
|
||||
XMLQuery query(templateDocument);
|
||||
|
||||
// XPath query to select all <tspan> nodes whose <text> parent
|
||||
// has "freecad:editable" attribute
|
||||
query.setQuery(QString::fromUtf8(
|
||||
query.processItems(QString::fromUtf8(
|
||||
"declare default element namespace \"" SVG_NS_URI "\"; "
|
||||
"declare namespace freecad=\"" FREECAD_SVG_NS_URI "\"; "
|
||||
"//text[@freecad:editable]/tspan"));
|
||||
|
||||
QXmlResultItems queryResult;
|
||||
query.evaluateTo(&queryResult);
|
||||
|
||||
while (!queryResult.next().isNull()) {
|
||||
QDomElement tspan = model.toDomNode(queryResult.current().toNodeModelIndex()).toElement();
|
||||
|
||||
"//text[@freecad:editable]/tspan"),
|
||||
[&editables](QDomElement& tspan) -> bool {
|
||||
QString editableName = tspan.parentNode().toElement().attribute(QString::fromUtf8("freecad:editable"));
|
||||
QString editableValue = tspan.firstChild().nodeValue();
|
||||
|
||||
editables[std::string(editableName.toUtf8().constData())] =
|
||||
std::string(editableValue.toUtf8().constData());
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
return editables;
|
||||
}
|
||||
|
||||
@@ -23,11 +23,7 @@
|
||||
#include "PreCompiled.h"
|
||||
#ifndef _PreComp_
|
||||
# include <sstream>
|
||||
|
||||
# include "QDomNodeModel.h"
|
||||
# include <QDomDocument>
|
||||
# include <QXmlResultItems>
|
||||
# include <QXmlQuery>
|
||||
#endif
|
||||
|
||||
#include <Base/Console.h>
|
||||
@@ -36,6 +32,7 @@
|
||||
#include "DrawViewSymbolPy.h" // generated from DrawViewSymbolPy.xml
|
||||
#include "DrawPage.h"
|
||||
#include "DrawUtil.h"
|
||||
#include "XMLQuery.h"
|
||||
|
||||
|
||||
using namespace TechDraw;
|
||||
@@ -105,30 +102,22 @@ bool DrawViewSymbol::checkFit(TechDraw::DrawPage* p) const
|
||||
std::vector<std::string> DrawViewSymbol::getEditableFields()
|
||||
{
|
||||
QDomDocument symbolDocument;
|
||||
QXmlResultItems queryResult;
|
||||
std::vector<std::string> editables;
|
||||
|
||||
bool rc = loadQDomDocument(symbolDocument);
|
||||
if (rc) {
|
||||
QDomElement symbolDocElem = symbolDocument.documentElement();
|
||||
QXmlQuery query(QXmlQuery::XQuery10);
|
||||
QDomNodeModel model(query.namePool(), symbolDocument);
|
||||
query.setFocus(QXmlItem(model.fromDomNode(symbolDocument.documentElement())));
|
||||
XMLQuery query(symbolDocument);
|
||||
|
||||
// XPath query to select all <tspan> nodes whose <text> parent
|
||||
// has "freecad:editable" attribute
|
||||
query.setQuery(QString::fromUtf8("declare default element namespace \"" SVG_NS_URI "\"; "
|
||||
"declare namespace freecad=\"" FREECAD_SVG_NS_URI "\"; "
|
||||
"//text[@freecad:editable]/tspan"));
|
||||
|
||||
query.evaluateTo(&queryResult);
|
||||
|
||||
while (!queryResult.next().isNull()) {
|
||||
QDomElement tspan =
|
||||
model.toDomNode(queryResult.current().toNodeModelIndex()).toElement();
|
||||
query.processItems(QString::fromUtf8("declare default element namespace \"" SVG_NS_URI "\"; "
|
||||
"declare namespace freecad=\"" FREECAD_SVG_NS_URI "\"; "
|
||||
"//text[@freecad:editable]/tspan"),
|
||||
[&editables](QDomElement& tspan) -> bool {
|
||||
QString editableValue = tspan.firstChild().nodeValue();
|
||||
editables.emplace_back(editableValue.toUtf8().constData());
|
||||
}
|
||||
editables.emplace_back(editableValue.toStdString());
|
||||
return true;
|
||||
});
|
||||
}
|
||||
return editables;
|
||||
}
|
||||
@@ -142,27 +131,22 @@ void DrawViewSymbol::updateFieldsInSymbol()
|
||||
}
|
||||
|
||||
QDomDocument symbolDocument;
|
||||
QXmlResultItems queryResult;
|
||||
|
||||
bool rc = loadQDomDocument(symbolDocument);
|
||||
if (rc) {
|
||||
QDomElement symbolDocElem = symbolDocument.documentElement();
|
||||
QXmlQuery query(QXmlQuery::XQuery10);
|
||||
QDomNodeModel model(query.namePool(), symbolDocument);
|
||||
query.setFocus(QXmlItem(model.fromDomNode(symbolDocElem)));
|
||||
XMLQuery query(symbolDocument);
|
||||
std::size_t count = 0;
|
||||
|
||||
// XPath query to select all <tspan> nodes whose <text> parent
|
||||
// has "freecad:editable" attribute
|
||||
query.setQuery(QString::fromUtf8("declare default element namespace \"" SVG_NS_URI "\"; "
|
||||
"declare namespace freecad=\"" FREECAD_SVG_NS_URI "\"; "
|
||||
"//text[@freecad:editable]/tspan"));
|
||||
query.evaluateTo(&queryResult);
|
||||
|
||||
unsigned int count = 0;
|
||||
while (!queryResult.next().isNull()) {
|
||||
QDomElement tspanElement =
|
||||
model.toDomNode(queryResult.current().toNodeModelIndex()).toElement();
|
||||
query.processItems(QString::fromUtf8("declare default element namespace \"" SVG_NS_URI "\"; "
|
||||
"declare namespace freecad=\"" FREECAD_SVG_NS_URI "\"; "
|
||||
"//text[@freecad:editable]/tspan"),
|
||||
[&symbolDocument, &editText, &count](QDomElement& tspanElement) -> bool {
|
||||
|
||||
if (count >= editText.size()) {
|
||||
return false;
|
||||
}
|
||||
// Keep all spaces in the text node
|
||||
tspanElement.setAttribute(QString::fromUtf8("xml:space"),
|
||||
QString::fromUtf8("preserve"));
|
||||
@@ -174,9 +158,11 @@ void DrawViewSymbol::updateFieldsInSymbol()
|
||||
|
||||
// Finally append text node with editable replacement as the only <tspan> descendant
|
||||
tspanElement.appendChild(
|
||||
symbolDocument.createTextNode(QString::fromUtf8(editText[count].c_str())));
|
||||
symbolDocument.createTextNode(QString::fromStdString(editText[count])));
|
||||
++count;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
Symbol.setValue(symbolDocument.toString(1).toStdString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
|
||||
#include <QDomDocument>
|
||||
#include <QDomNode>
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
|
||||
#include "QDomNodeModel.h"
|
||||
#include <QSourceLocation>
|
||||
#include <QUrl>
|
||||
@@ -359,3 +360,4 @@ QXmlNodeModelIndex QDomNodeModel::nextFromSimpleAxis ( SimpleAxis axis, const QX
|
||||
|
||||
return QXmlNodeModelIndex();
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2023 Werner Mayer <wmayer[at]users.sourceforge.net> *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "PreCompiled.h"
|
||||
|
||||
#ifndef _PreComp_
|
||||
# include <QDomDocument>
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
|
||||
# include "QDomNodeModel.h"
|
||||
# include <QXmlQuery>
|
||||
# include <QXmlResultItems>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "XMLQuery.h"
|
||||
|
||||
|
||||
using namespace TechDraw;
|
||||
|
||||
XMLQuery::XMLQuery(QDomDocument& dom)
|
||||
: domDocument(dom)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
|
||||
bool XMLQuery::processItems(const QString& queryStr, const std::function<bool(QDomElement&)>& process)
|
||||
{
|
||||
QXmlQuery query(QXmlQuery::XQuery10);
|
||||
QDomNodeModel model(query.namePool(), domDocument);
|
||||
QDomElement symbolDocElem = domDocument.documentElement();
|
||||
query.setFocus(QXmlItem(model.fromDomNode(symbolDocElem)));
|
||||
|
||||
query.setQuery(queryStr);
|
||||
QXmlResultItems queryResult;
|
||||
query.evaluateTo(&queryResult);
|
||||
|
||||
while (!queryResult.next().isNull()) {
|
||||
QDomElement tspanElement =
|
||||
model.toDomNode(queryResult.current().toNodeModelIndex()).toElement();
|
||||
if (!process(tspanElement)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
bool XMLQuery::processItems(const QString& queryStr, const std::function<bool(QDomElement&)>& process)
|
||||
{
|
||||
//TODO: Port to Qt6
|
||||
Q_UNUSED(queryStr)
|
||||
Q_UNUSED(process)
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,49 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2023 Werner Mayer <wmayer[at]users.sourceforge.net> *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef TECHDRAW_XMLQuery_h_
|
||||
#define TECHDRAW_XMLQuery_h_
|
||||
|
||||
#include <Mod/TechDraw/TechDrawGlobal.h>
|
||||
#include <QList>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QDomDocument;
|
||||
class QDomElement;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace TechDraw
|
||||
{
|
||||
|
||||
class TechDrawExport XMLQuery
|
||||
{
|
||||
public:
|
||||
XMLQuery(QDomDocument&);
|
||||
bool processItems(const QString& queryStr, const std::function<bool(QDomElement&)>& process);
|
||||
|
||||
private:
|
||||
QDomDocument& domDocument;
|
||||
};
|
||||
|
||||
} //namespace TechDraw
|
||||
|
||||
#endif //TECHDRAW_XMLQuery_h_
|
||||
@@ -29,8 +29,6 @@
|
||||
# include <QGraphicsSvgItem>
|
||||
# include <QPen>
|
||||
# include <QSvgRenderer>
|
||||
# include <QXmlQuery>
|
||||
# include <QXmlResultItems>
|
||||
#endif// #ifndef _PreComp_
|
||||
|
||||
#include <App/Application.h>
|
||||
@@ -39,7 +37,7 @@
|
||||
|
||||
#include <Mod/TechDraw/App/DrawSVGTemplate.h>
|
||||
#include <Mod/TechDraw/App/DrawUtil.h>
|
||||
#include <Mod/TechDraw/App/QDomNodeModel.h>
|
||||
#include <Mod/TechDraw/App/XMLQuery.h>
|
||||
|
||||
#include "QGISVGTemplate.h"
|
||||
#include "PreferencesGui.h"
|
||||
@@ -165,20 +163,6 @@ void QGISVGTemplate::createClickHandles()
|
||||
}
|
||||
file.close();
|
||||
|
||||
QDomElement templateDocElem = templateDocument.documentElement();
|
||||
|
||||
QXmlQuery query(QXmlQuery::XQuery10);
|
||||
QDomNodeModel model(query.namePool(), templateDocument);
|
||||
query.setFocus(QXmlItem(model.fromDomNode(templateDocElem)));
|
||||
|
||||
// XPath query to select all <text> nodes with "freecad:editable" attribute
|
||||
query.setQuery(QString::fromUtf8("declare default element namespace \"" SVG_NS_URI "\"; "
|
||||
"declare namespace freecad=\"" FREECAD_SVG_NS_URI "\"; "
|
||||
"//text[@freecad:editable]"));
|
||||
|
||||
QXmlResultItems queryResult;
|
||||
query.evaluateTo(&queryResult);
|
||||
|
||||
//TODO: Find location of special fields (first/third angle) and make graphics items for them
|
||||
|
||||
Base::Reference<ParameterGrp> hGrp = App::GetApplication()
|
||||
@@ -194,10 +178,13 @@ void QGISVGTemplate::createClickHandles()
|
||||
double width = editClickBoxSize;
|
||||
double height = editClickBoxSize;
|
||||
|
||||
while (!queryResult.next().isNull()) {
|
||||
QDomElement textElement =
|
||||
model.toDomNode(queryResult.current().toNodeModelIndex()).toElement();
|
||||
TechDraw::XMLQuery query(templateDocument);
|
||||
|
||||
// XPath query to select all <text> nodes with "freecad:editable" attribute
|
||||
query.processItems(QString::fromUtf8("declare default element namespace \"" SVG_NS_URI "\"; "
|
||||
"declare namespace freecad=\"" FREECAD_SVG_NS_URI "\"; "
|
||||
"//text[@freecad:editable]"),
|
||||
[&](QDomElement& textElement) -> bool {
|
||||
QString name = textElement.attribute(QString::fromUtf8("freecad:editable"));
|
||||
double x = Rez::guiX(
|
||||
textElement.attribute(QString::fromUtf8("x"), QString::fromUtf8("0.0")).toDouble());
|
||||
@@ -207,7 +194,7 @@ void QGISVGTemplate::createClickHandles()
|
||||
if (name.isEmpty()) {
|
||||
Base::Console().Warning(
|
||||
"QGISVGTemplate::createClickHandles - no name for editable text at %f, %f\n", x, y);
|
||||
continue;
|
||||
return true;
|
||||
}
|
||||
|
||||
auto item(new TemplateTextField(this, svgTemplate, name.toStdString()));
|
||||
@@ -229,7 +216,8 @@ void QGISVGTemplate::createClickHandles()
|
||||
addToGroup(item);
|
||||
|
||||
textFields.push_back(item);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
#include <Mod/TechDraw/Gui/moc_QGISVGTemplate.cpp>
|
||||
|
||||
@@ -32,9 +32,9 @@ class DrawViewDimensionTest(unittest.TestCase):
|
||||
self.view1.Source = [self.document.Sphere]
|
||||
self.view1.X = 220
|
||||
self.view1.Y = 150
|
||||
|
||||
|
||||
self.document.recompute()
|
||||
|
||||
|
||||
#wait for threads to complete before checking result
|
||||
loop = QtCore.QEventLoop()
|
||||
|
||||
@@ -53,7 +53,7 @@ class DrawViewDimensionTest(unittest.TestCase):
|
||||
"""Tests if a length dimension can be added to view"""
|
||||
# make length dimension
|
||||
print("making length dimension")
|
||||
|
||||
|
||||
dimension = self.document.addObject("TechDraw::DrawViewDimension", "Dimension")
|
||||
self.page.addView(dimension)
|
||||
dimension.Type = "Distance"
|
||||
|
||||
@@ -6,7 +6,7 @@ def createPageWithSVGTemplate(doc=None):
|
||||
"""Returns a page with an SVGTemplate added on the ActiveDocument"""
|
||||
path = os.path.dirname(os.path.abspath(__file__))
|
||||
templateFileSpec = path + "/TestTemplate.svg"
|
||||
|
||||
|
||||
if not doc:
|
||||
doc = FreeCAD.ActiveDocument
|
||||
|
||||
|
||||
@@ -49,8 +49,8 @@ STDAPI CreateRegistryKeys(REGKEY_SUBKEY_AND_VALUE* aKeys, ULONG cKeys);
|
||||
STDAPI DeleteRegistryKeys(REGKEY_DELETEKEY* aKeys, ULONG cKeys);
|
||||
|
||||
|
||||
BOOL APIENTRY DllMain(HINSTANCE hinstDll,
|
||||
DWORD dwReason,
|
||||
BOOL APIENTRY DllMain(HINSTANCE hinstDll,
|
||||
DWORD dwReason,
|
||||
LPVOID pvReserved)
|
||||
{
|
||||
switch (dwReason)
|
||||
@@ -88,8 +88,8 @@ STDAPI_(ULONG) DllRelease()
|
||||
|
||||
STDAPI DllRegisterServer()
|
||||
{
|
||||
// This tells the shell to invalidate the thumbnail cache. This is important because any .recipe files
|
||||
// viewed before registering this handler would otherwise show cached blank thumbnails.
|
||||
// This tells the shell to invalidate the thumbnail cache. This is important because any .recipe files
|
||||
// viewed before registering this handler would otherwise show cached blank thumbnails.
|
||||
SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
|
||||
|
||||
WCHAR szModule[MAX_PATH];
|
||||
@@ -101,12 +101,12 @@ STDAPI DllRegisterServer()
|
||||
REGKEY_SUBKEY_AND_VALUE keys[] = {
|
||||
{HKEY_CLASSES_ROOT, L"CLSID\\" szCLSID_SampleThumbnailProvider, NULL, REG_SZ, (DWORD_PTR)L"FCStd Thumbnail Provider"},
|
||||
#if 1
|
||||
//{HKEY_CLASSES_ROOT, L"CLSID\\DisableProcessIsolation", NULL, REG_DWORD, (DWORD) 1},
|
||||
{HKEY_CLASSES_ROOT, L"CLSID\\" szCLSID_SampleThumbnailProvider, L"DisableProcessIsolation", REG_DWORD, (DWORD) 1},
|
||||
//{HKEY_CLASSES_ROOT, L"CLSID\\DisableProcessIsolation", NULL, REG_DWORD, (DWORD) 1},
|
||||
{HKEY_CLASSES_ROOT, L"CLSID\\" szCLSID_SampleThumbnailProvider, L"DisableProcessIsolation", REG_DWORD, (DWORD) 1},
|
||||
#endif
|
||||
{HKEY_CLASSES_ROOT, L"CLSID\\" szCLSID_SampleThumbnailProvider L"\\InprocServer32", NULL, REG_SZ, (DWORD_PTR)szModule},
|
||||
{HKEY_CLASSES_ROOT, L"CLSID\\" szCLSID_SampleThumbnailProvider L"\\InprocServer32", L"ThreadingModel", REG_SZ, (DWORD_PTR)L"Apartment"},
|
||||
//{HKEY_CLASSES_ROOT, L".FCStd\\shellex", L"Trick only here to create shellex when not existing",REG_DWORD, 1},
|
||||
//{HKEY_CLASSES_ROOT, L".FCStd\\shellex", L"Trick only here to create shellex when not existing",REG_DWORD, 1},
|
||||
{HKEY_CLASSES_ROOT, L".FCStd\\shellex\\{E357FCCD-A995-4576-B01F-234630154E96}", NULL, REG_SZ, (DWORD_PTR)szCLSID_SampleThumbnailProvider},
|
||||
{HKEY_CLASSES_ROOT, L".FCBak\\shellex\\{E357FCCD-A995-4576-B01F-234630154E96}", NULL, REG_SZ, (DWORD_PTR)szCLSID_SampleThumbnailProvider}
|
||||
};
|
||||
@@ -143,7 +143,7 @@ STDAPI CreateRegistryKey(REGKEY_SUBKEY_AND_VALUE* pKey)
|
||||
cbData += sizeof(WCHAR);
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
hr = E_INVALIDARG;
|
||||
}
|
||||
|
||||
@@ -178,7 +178,7 @@ CThumbnailProvider::~CThumbnailProvider()
|
||||
STDMETHODIMP CThumbnailProvider::QueryInterface(REFIID riid,
|
||||
void** ppvObject)
|
||||
{
|
||||
static const QITAB qit[] =
|
||||
static const QITAB qit[] =
|
||||
{
|
||||
//QITABENT(CThumbnailProvider, IInitializeWithStream),
|
||||
QITABENT(CThumbnailProvider, IInitializeWithFile),
|
||||
@@ -206,16 +206,16 @@ STDMETHODIMP_(ULONG) CThumbnailProvider::Release()
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CThumbnailProvider::Initialize(IStream *pstm,
|
||||
STDMETHODIMP CThumbnailProvider::Initialize(IStream *pstm,
|
||||
DWORD grfMode)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP CThumbnailProvider::Initialize(LPCWSTR pszFilePath,
|
||||
STDMETHODIMP CThumbnailProvider::Initialize(LPCWSTR pszFilePath,
|
||||
DWORD grfMode)
|
||||
{
|
||||
wcscpy_s(m_szFile, pszFilePath);
|
||||
wcscpy_s(m_szFile, pszFilePath);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
@@ -235,8 +235,8 @@ bool CThumbnailProvider::CheckZip() const
|
||||
return true;
|
||||
}
|
||||
|
||||
STDMETHODIMP CThumbnailProvider::GetThumbnail(UINT cx,
|
||||
HBITMAP *phbmp,
|
||||
STDMETHODIMP CThumbnailProvider::GetThumbnail(UINT cx,
|
||||
HBITMAP *phbmp,
|
||||
WTS_ALPHATYPE *pdwAlpha)
|
||||
{
|
||||
try {
|
||||
@@ -277,11 +277,11 @@ STDMETHODIMP CThumbnailProvider::GetThumbnail(UINT cx,
|
||||
// or whatever could go wrong
|
||||
}
|
||||
|
||||
return NOERROR;
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
|
||||
STDMETHODIMP CThumbnailProvider::GetSite(REFIID riid,
|
||||
STDMETHODIMP CThumbnailProvider::GetSite(REFIID riid,
|
||||
void** ppvSite)
|
||||
{
|
||||
if (m_pSite)
|
||||
|
||||
@@ -5,5 +5,6 @@ target_sources(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Expression.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/IndexedName.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/License.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/MappedName.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Metadata.cpp
|
||||
)
|
||||
|
||||
@@ -0,0 +1,832 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "App/MappedName.h"
|
||||
#include "App/ComplexGeoData.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
// NOLINTBEGIN(readability-magic-numbers)
|
||||
|
||||
TEST(MappedName, defaultConstruction)
|
||||
{
|
||||
// Act
|
||||
Data::MappedName mappedName;
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(mappedName.isRaw(), false);
|
||||
EXPECT_EQ(mappedName.empty(), true);
|
||||
EXPECT_EQ(mappedName.size(), 0);
|
||||
EXPECT_EQ(mappedName.dataBytes(), QByteArray());
|
||||
EXPECT_EQ(mappedName.postfixBytes(), QByteArray());
|
||||
}
|
||||
|
||||
TEST(MappedName, namedConstruction)
|
||||
{
|
||||
// Act
|
||||
Data::MappedName mappedName("TEST");
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(mappedName.isRaw(), false);
|
||||
EXPECT_EQ(mappedName.empty(), false);
|
||||
EXPECT_EQ(mappedName.size(), 4);
|
||||
EXPECT_EQ(mappedName.dataBytes(), QByteArray("TEST"));
|
||||
EXPECT_EQ(mappedName.postfixBytes(), QByteArray());
|
||||
}
|
||||
|
||||
TEST(MappedName, namedConstructionWithMaxSize)
|
||||
{
|
||||
// Act
|
||||
Data::MappedName mappedName("TEST", 2);
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(mappedName.isRaw(), false);
|
||||
EXPECT_EQ(mappedName.empty(), false);
|
||||
EXPECT_EQ(mappedName.size(), 2);
|
||||
EXPECT_EQ(mappedName.dataBytes(), QByteArray("TE"));
|
||||
EXPECT_EQ(mappedName.postfixBytes(), QByteArray());
|
||||
}
|
||||
|
||||
TEST(MappedName, namedConstructionDiscardPrefix)
|
||||
{
|
||||
// Arrange
|
||||
std::string name = Data::ComplexGeoData::elementMapPrefix() + "TEST";
|
||||
|
||||
// Act
|
||||
Data::MappedName mappedName(name.c_str());
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(mappedName.isRaw(), false);
|
||||
EXPECT_EQ(mappedName.empty(), false);
|
||||
EXPECT_EQ(mappedName.size(), 4);
|
||||
EXPECT_EQ(mappedName.dataBytes(), QByteArray("TEST"));
|
||||
EXPECT_EQ(mappedName.postfixBytes(), QByteArray());
|
||||
}
|
||||
|
||||
TEST(MappedName, stringNamedConstruction)
|
||||
{
|
||||
// Act
|
||||
Data::MappedName mappedName(std::string("TEST"));
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(mappedName.isRaw(), false);
|
||||
EXPECT_EQ(mappedName.empty(), false);
|
||||
EXPECT_EQ(mappedName.size(), 4);
|
||||
EXPECT_EQ(mappedName.dataBytes(), QByteArray("TEST"));
|
||||
EXPECT_EQ(mappedName.postfixBytes(), QByteArray());
|
||||
}
|
||||
|
||||
TEST(MappedName, stringNamedConstructionDiscardPrefix)
|
||||
{
|
||||
// Arrange
|
||||
std::string name = Data::ComplexGeoData::elementMapPrefix() + "TEST";
|
||||
|
||||
// Act
|
||||
Data::MappedName mappedName(name);
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(mappedName.isRaw(), false);
|
||||
EXPECT_EQ(mappedName.empty(), false);
|
||||
EXPECT_EQ(mappedName.size(), 4);
|
||||
EXPECT_EQ(mappedName.dataBytes(), QByteArray("TEST"));
|
||||
EXPECT_EQ(mappedName.postfixBytes(), QByteArray());
|
||||
}
|
||||
|
||||
TEST(MappedName, constructFromIndexedNameNoIndex)
|
||||
{
|
||||
// Arrange
|
||||
Data::IndexedName indexedName {"INDEXED_NAME"};
|
||||
|
||||
// Act
|
||||
Data::MappedName mappedName {indexedName};
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(mappedName.dataBytes().constData(), indexedName.getType()); // shared memory
|
||||
EXPECT_EQ(mappedName.isRaw(), true);
|
||||
}
|
||||
|
||||
TEST(MappedName, constructFromIndexedNameWithIndex)
|
||||
{
|
||||
// Arrange
|
||||
Data::IndexedName indexedName {"INDEXED_NAME", 1};
|
||||
|
||||
// Act
|
||||
Data::MappedName mappedName {indexedName};
|
||||
|
||||
// Assert
|
||||
EXPECT_NE(mappedName.dataBytes().constData(), indexedName.getType()); // NOT shared memory
|
||||
EXPECT_EQ(mappedName.isRaw(), false);
|
||||
EXPECT_EQ(mappedName.toString(), indexedName.toString());
|
||||
}
|
||||
|
||||
TEST(MappedName, copyConstructor)
|
||||
{
|
||||
// Arrange
|
||||
Data::MappedName temp("TEST");
|
||||
|
||||
// Act
|
||||
Data::MappedName mappedName(temp);
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(mappedName.isRaw(), false);
|
||||
EXPECT_EQ(mappedName.empty(), false);
|
||||
EXPECT_EQ(mappedName.size(), 4);
|
||||
EXPECT_EQ(mappedName.dataBytes(), QByteArray("TEST"));
|
||||
EXPECT_EQ(mappedName.postfixBytes(), QByteArray());
|
||||
}
|
||||
|
||||
TEST(MappedName, copyConstructorWithPostfix)
|
||||
{
|
||||
// Arrange
|
||||
Data::MappedName temp("TEST");
|
||||
|
||||
// Act
|
||||
Data::MappedName mappedName(temp, "POSTFIXTEST");
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(mappedName.isRaw(), false);
|
||||
EXPECT_EQ(mappedName.empty(), false);
|
||||
EXPECT_EQ(mappedName.size(), 15);
|
||||
EXPECT_EQ(mappedName.dataBytes(), QByteArray("TEST"));
|
||||
EXPECT_EQ(mappedName.postfixBytes(), QByteArray("POSTFIXTEST"));
|
||||
|
||||
// Act
|
||||
Data::MappedName mappedName2(mappedName, "ANOTHERPOSTFIX");
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(mappedName2.isRaw(), false);
|
||||
EXPECT_EQ(mappedName2.empty(), false);
|
||||
EXPECT_EQ(mappedName2.size(), 29);
|
||||
EXPECT_EQ(mappedName2.dataBytes(), QByteArray("TESTPOSTFIXTEST"));
|
||||
EXPECT_EQ(mappedName2.postfixBytes(), QByteArray("ANOTHERPOSTFIX"));
|
||||
}
|
||||
|
||||
TEST(MappedName, copyConstructorStartpos)
|
||||
{
|
||||
// Arrange
|
||||
Data::MappedName temp(Data::MappedName("TEST"), "POSTFIXTEST");
|
||||
|
||||
// Act
|
||||
Data::MappedName mappedName(temp, 2, -1);
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(mappedName.isRaw(), false);
|
||||
EXPECT_EQ(mappedName.empty(), false);
|
||||
EXPECT_EQ(mappedName.size(), 13);
|
||||
EXPECT_EQ(mappedName.dataBytes(), QByteArray("ST"));
|
||||
EXPECT_EQ(mappedName.postfixBytes(), QByteArray("POSTFIXTEST"));
|
||||
}
|
||||
|
||||
TEST(MappedName, copyConstructorStartposAndSize)
|
||||
{
|
||||
// Arrange
|
||||
Data::MappedName temp(Data::MappedName("TEST"), "POSTFIXTEST");
|
||||
|
||||
// Act
|
||||
Data::MappedName mappedName(temp, 2, 6);
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(mappedName.isRaw(), false);
|
||||
EXPECT_EQ(mappedName.empty(), false);
|
||||
EXPECT_EQ(mappedName.size(), 6);
|
||||
EXPECT_EQ(mappedName.dataBytes(), QByteArray("ST"));
|
||||
EXPECT_EQ(mappedName.postfixBytes(), QByteArray("POST"));
|
||||
}
|
||||
|
||||
TEST(MappedName, moveConstructor)
|
||||
{
|
||||
// Arrange
|
||||
Data::MappedName temp(Data::MappedName("TEST"), "POSTFIXTEST");
|
||||
|
||||
// Act
|
||||
Data::MappedName mappedName(std::move(temp));
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(mappedName.isRaw(), false);
|
||||
EXPECT_EQ(mappedName.empty(), false);
|
||||
EXPECT_EQ(mappedName.size(), 15);
|
||||
EXPECT_EQ(mappedName.dataBytes(), QByteArray("TEST"));
|
||||
EXPECT_EQ(mappedName.postfixBytes(), QByteArray("POSTFIXTEST"));
|
||||
}
|
||||
|
||||
TEST(MappedName, fromRawData)
|
||||
{
|
||||
// Act
|
||||
Data::MappedName mappedName = Data::MappedName::fromRawData("TESTTEST", 10);
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(mappedName.isRaw(), true);
|
||||
EXPECT_EQ(mappedName.empty(), false);
|
||||
EXPECT_EQ(mappedName.size(), 10);
|
||||
EXPECT_EQ(mappedName.dataBytes(), QByteArray("TESTTEST", 10));
|
||||
EXPECT_EQ(mappedName.postfixBytes(), QByteArray());
|
||||
}
|
||||
|
||||
TEST(MappedName, fromRawDataQByteArray)
|
||||
{
|
||||
// Act
|
||||
Data::MappedName mappedName = Data::MappedName::fromRawData(QByteArray("TESTTEST", 10));
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(mappedName.isRaw(), true);
|
||||
EXPECT_EQ(mappedName.empty(), false);
|
||||
EXPECT_EQ(mappedName.size(), 10);
|
||||
EXPECT_EQ(mappedName.dataBytes(), QByteArray("TESTTEST", 10));
|
||||
EXPECT_EQ(mappedName.postfixBytes(), QByteArray());
|
||||
}
|
||||
|
||||
TEST(MappedName, fromRawDataCopy)
|
||||
{
|
||||
// Arrange
|
||||
Data::MappedName temp = Data::MappedName::fromRawData(QByteArray("TESTTEST", 10));
|
||||
temp.append("TESTPOSTFIX");
|
||||
temp.compact(); //Always call compact before accessing data!
|
||||
|
||||
// Act
|
||||
Data::MappedName mappedName = Data::MappedName::fromRawData(temp, 0);
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(mappedName.isRaw(), true);
|
||||
EXPECT_EQ(mappedName.empty(), false);
|
||||
EXPECT_EQ(mappedName.size(), 21);
|
||||
EXPECT_EQ(mappedName.dataBytes(), QByteArray("TESTTEST", 10));
|
||||
EXPECT_EQ(mappedName.postfixBytes(), QByteArray("TESTPOSTFIX"));
|
||||
}
|
||||
|
||||
TEST(MappedName, fromRawDataCopyStartposAndSize)
|
||||
{
|
||||
// Arrange
|
||||
Data::MappedName temp = Data::MappedName::fromRawData(QByteArray("TESTTEST", 8));
|
||||
temp.append("ABCDEFGHIJKLM"); //postfix
|
||||
temp.compact(); //Always call compact before accessing data!
|
||||
|
||||
// Act
|
||||
Data::MappedName mappedName = Data::MappedName::fromRawData(temp, 2, 13);
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(mappedName.isRaw(), true);
|
||||
EXPECT_EQ(mappedName.empty(), false);
|
||||
EXPECT_EQ(mappedName.size(), 13);
|
||||
EXPECT_EQ(mappedName.dataBytes(), QByteArray("STTEST", 6));
|
||||
EXPECT_EQ(mappedName.postfixBytes(), QByteArray("ABCDEFG"));
|
||||
}
|
||||
|
||||
TEST(MappedName, assignmentOperator)
|
||||
{
|
||||
// Arrange
|
||||
Data::MappedName temp(Data::MappedName("TEST"), "POSTFIXTEST");
|
||||
|
||||
// Act
|
||||
Data::MappedName mappedName = temp;
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(mappedName.isRaw(), false);
|
||||
EXPECT_EQ(mappedName.empty(), false);
|
||||
EXPECT_EQ(mappedName.size(), 15);
|
||||
EXPECT_EQ(mappedName.dataBytes(), QByteArray("TEST"));
|
||||
EXPECT_EQ(mappedName.postfixBytes(), QByteArray("POSTFIXTEST"));
|
||||
}
|
||||
|
||||
TEST(MappedName, assignmentOperatorString)
|
||||
{
|
||||
// Arrange
|
||||
Data::MappedName mappedName;
|
||||
|
||||
// Act
|
||||
mappedName = std::string("TEST");
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(mappedName.isRaw(), false);
|
||||
EXPECT_EQ(mappedName.empty(), false);
|
||||
EXPECT_EQ(mappedName.size(), 4);
|
||||
EXPECT_EQ(mappedName.dataBytes(), QByteArray("TEST"));
|
||||
EXPECT_EQ(mappedName.postfixBytes(), QByteArray());
|
||||
}
|
||||
|
||||
TEST(MappedName, assignmentOperatorConstCharPtr)
|
||||
{
|
||||
// Arrange
|
||||
Data::MappedName mappedName;
|
||||
|
||||
// Act
|
||||
mappedName = "TEST";
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(mappedName.isRaw(), false);
|
||||
EXPECT_EQ(mappedName.empty(), false);
|
||||
EXPECT_EQ(mappedName.size(), 4);
|
||||
EXPECT_EQ(mappedName.dataBytes(), QByteArray("TEST"));
|
||||
EXPECT_EQ(mappedName.postfixBytes(), QByteArray());
|
||||
}
|
||||
|
||||
TEST(MappedName, assignmentOperatorMove)
|
||||
{
|
||||
// Arrange
|
||||
Data::MappedName temp(Data::MappedName("TEST"), "POSTFIXTEST");
|
||||
|
||||
// Act
|
||||
Data::MappedName mappedName = std::move(temp);
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(mappedName.isRaw(), false);
|
||||
EXPECT_EQ(mappedName.empty(), false);
|
||||
EXPECT_EQ(mappedName.size(), 15);
|
||||
EXPECT_EQ(mappedName.dataBytes(), QByteArray("TEST"));
|
||||
EXPECT_EQ(mappedName.postfixBytes(), QByteArray("POSTFIXTEST"));
|
||||
}
|
||||
|
||||
TEST(MappedName, streamInsertionOperator)
|
||||
{
|
||||
// Arrange
|
||||
Data::MappedName mappedName(Data::MappedName("TEST"), "POSTFIXTEST");
|
||||
std::stringstream ss;
|
||||
|
||||
// Act
|
||||
ss << mappedName;
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(ss.str(), std::string("TESTPOSTFIXTEST"));
|
||||
}
|
||||
|
||||
TEST(MappedName, comparisonOperators)
|
||||
{
|
||||
// Arrange
|
||||
Data::MappedName mappedName1(Data::MappedName("TEST"), "POSTFIXTEST");
|
||||
Data::MappedName mappedName2(Data::MappedName("TEST"), "POSTFIXTEST");
|
||||
Data::MappedName mappedName3(Data::MappedName("TESTPOST"), "FIXTEST");
|
||||
Data::MappedName mappedName4(Data::MappedName("THIS"), "ISDIFFERENT");
|
||||
|
||||
// Act & Assert
|
||||
EXPECT_EQ(mappedName1 == mappedName1, true);
|
||||
EXPECT_EQ(mappedName1 == mappedName2, true);
|
||||
EXPECT_EQ(mappedName1 == mappedName3, true);
|
||||
EXPECT_EQ(mappedName1 == mappedName4, false);
|
||||
|
||||
EXPECT_EQ(mappedName1 != mappedName1, false);
|
||||
EXPECT_EQ(mappedName1 != mappedName2, false);
|
||||
EXPECT_EQ(mappedName1 != mappedName3, false);
|
||||
EXPECT_EQ(mappedName1 != mappedName4, true);
|
||||
}
|
||||
|
||||
TEST(MappedName, additionOperators)
|
||||
{
|
||||
// Arrange
|
||||
Data::MappedName mappedName(Data::MappedName("TEST"), "POSTFIXTEST");
|
||||
|
||||
// Act
|
||||
mappedName += "POST1";
|
||||
mappedName += std::string("POST2");
|
||||
mappedName += QByteArray("POST3");
|
||||
mappedName += Data::MappedName("POST4");
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(mappedName.isRaw(), false);
|
||||
EXPECT_EQ(mappedName.empty(), false);
|
||||
EXPECT_EQ(mappedName.size(), 35);
|
||||
EXPECT_EQ(mappedName.dataBytes(), QByteArray("TEST"));
|
||||
EXPECT_EQ(mappedName.postfixBytes(), QByteArray("POSTFIXTESTPOST1POST2POST3POST4"));
|
||||
|
||||
// Arrange
|
||||
mappedName = Data::MappedName(Data::MappedName("TEST"), "POSTFIXTEST");
|
||||
|
||||
// Act
|
||||
mappedName = mappedName + Data::MappedName("POST5");
|
||||
mappedName = mappedName + "POST6";
|
||||
mappedName = mappedName + std::string("POST7");
|
||||
mappedName = mappedName + QByteArray("POST8");
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(mappedName.isRaw(), false);
|
||||
EXPECT_EQ(mappedName.empty(), false);
|
||||
EXPECT_EQ(mappedName.size(), 35);
|
||||
EXPECT_EQ(mappedName.dataBytes(), QByteArray("TEST"));
|
||||
EXPECT_EQ(mappedName.postfixBytes(), QByteArray("POSTFIXTESTPOST5POST6POST7POST8"));
|
||||
}
|
||||
|
||||
TEST(MappedName, append)
|
||||
{
|
||||
// Arrange
|
||||
Data::MappedName mappedName;
|
||||
|
||||
// Act
|
||||
mappedName.append("TEST");
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(mappedName.isRaw(), false);
|
||||
EXPECT_EQ(mappedName.empty(), false);
|
||||
EXPECT_EQ(mappedName.size(), 4);
|
||||
EXPECT_EQ(mappedName.dataBytes(), QByteArray("TEST"));
|
||||
EXPECT_EQ(mappedName.postfixBytes(), QByteArray(""));
|
||||
|
||||
// Act
|
||||
mappedName.append("POSTFIX");
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(mappedName.isRaw(), false);
|
||||
EXPECT_EQ(mappedName.empty(), false);
|
||||
EXPECT_EQ(mappedName.size(), 11);
|
||||
EXPECT_EQ(mappedName.dataBytes(), QByteArray("TEST"));
|
||||
EXPECT_EQ(mappedName.postfixBytes(), QByteArray("POSTFIX"));
|
||||
|
||||
// Act
|
||||
mappedName.append("ANOTHERPOSTFIX", 5);
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(mappedName.isRaw(), false);
|
||||
EXPECT_EQ(mappedName.empty(), false);
|
||||
EXPECT_EQ(mappedName.size(), 16);
|
||||
EXPECT_EQ(mappedName.dataBytes(), QByteArray("TEST"));
|
||||
EXPECT_EQ(mappedName.postfixBytes(), QByteArray("POSTFIXANOTH"));
|
||||
}
|
||||
|
||||
TEST(MappedName, appendMappedNameObj)
|
||||
{
|
||||
// Arrange
|
||||
Data::MappedName mappedName;
|
||||
Data::MappedName temp(Data::MappedName("TEST"), "POSTFIXTEST");
|
||||
|
||||
// Act
|
||||
mappedName.append(temp);
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(mappedName.isRaw(), false);
|
||||
EXPECT_EQ(mappedName.empty(), false);
|
||||
EXPECT_EQ(mappedName.size(), 15);
|
||||
EXPECT_EQ(mappedName.dataBytes(), QByteArray("TEST"));
|
||||
EXPECT_EQ(mappedName.postfixBytes(), QByteArray("POSTFIXTEST"));
|
||||
|
||||
// Act
|
||||
mappedName.append(temp, 2, 7);
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(mappedName.isRaw(), false);
|
||||
EXPECT_EQ(mappedName.empty(), false);
|
||||
EXPECT_EQ(mappedName.size(), 22);
|
||||
EXPECT_EQ(mappedName.dataBytes(), QByteArray("TEST"));
|
||||
EXPECT_EQ(mappedName.postfixBytes(), QByteArray("POSTFIXTESTSTPOSTF"));
|
||||
}
|
||||
|
||||
TEST(MappedName, toString)
|
||||
{
|
||||
// Arrange
|
||||
Data::MappedName mappedName(Data::MappedName("TEST"), "POSTFIXTEST");
|
||||
|
||||
// Act & Assert
|
||||
EXPECT_EQ(mappedName.toString(0), "TESTPOSTFIXTEST");
|
||||
EXPECT_EQ(mappedName.toString(0), std::string("TESTPOSTFIXTEST"));
|
||||
EXPECT_EQ(mappedName.toString(2, 8), "STPOSTFI");
|
||||
EXPECT_EQ(mappedName.toString(2, 8), std::string("STPOSTFI"));
|
||||
}
|
||||
|
||||
TEST(MappedName, toConstString)
|
||||
{
|
||||
// Arrange
|
||||
Data::MappedName mappedName(Data::MappedName("TEST"), "POSTFIXTEST");
|
||||
int size{0};
|
||||
|
||||
// Act
|
||||
const char *temp = mappedName.toConstString(0, size);
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(QByteArray(temp, size), QByteArray("TEST"));
|
||||
EXPECT_EQ(size, 4);
|
||||
|
||||
// Act
|
||||
const char *temp2 = mappedName.toConstString(7, size);
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(QByteArray(temp2, size), QByteArray("TFIXTEST"));
|
||||
EXPECT_EQ(size, 8);
|
||||
}
|
||||
|
||||
TEST(MappedName, toRawBytes)
|
||||
{
|
||||
// Arrange
|
||||
Data::MappedName mappedName(Data::MappedName("TEST"), "POSTFIXTEST");
|
||||
|
||||
// Act & Assert
|
||||
EXPECT_EQ(mappedName.toRawBytes(), QByteArray("TESTPOSTFIXTEST"));
|
||||
EXPECT_EQ(mappedName.toRawBytes(3), QByteArray("TPOSTFIXTEST"));
|
||||
EXPECT_EQ(mappedName.toRawBytes(7, 3), QByteArray("TFI"));
|
||||
EXPECT_EQ(mappedName.toRawBytes(502, 5), QByteArray());
|
||||
}
|
||||
|
||||
TEST(MappedName, toIndexedNameASCIIOnly)
|
||||
{
|
||||
// Arrange
|
||||
Data::MappedName mappedName {"MAPPED_NAME"};
|
||||
|
||||
// Act
|
||||
auto indexedName = mappedName.toIndexedName();
|
||||
|
||||
// Assert
|
||||
EXPECT_FALSE(indexedName.isNull());
|
||||
}
|
||||
|
||||
TEST(MappedName, toIndexedNameInvalid)
|
||||
{
|
||||
// Arrange
|
||||
Data::MappedName mappedName {"MAPPED-NAME"};
|
||||
|
||||
// Act
|
||||
auto indexedName = mappedName.toIndexedName();
|
||||
|
||||
// Assert
|
||||
EXPECT_TRUE(indexedName.isNull());
|
||||
}
|
||||
|
||||
TEST(MappedName, appendToBuffer)
|
||||
{
|
||||
// Arrange
|
||||
Data::MappedName mappedName(Data::MappedName("TEST"), "POSTFIXTEST");
|
||||
std::string buffer("STUFF");
|
||||
|
||||
// Act
|
||||
mappedName.appendToBuffer(buffer);
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(buffer, std::string("STUFFTESTPOSTFIXTEST"));
|
||||
|
||||
// Act
|
||||
mappedName.appendToBuffer(buffer, 2, 7);
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(buffer, std::string("STUFFTESTPOSTFIXTESTSTPOSTF"));
|
||||
}
|
||||
|
||||
TEST(MappedName, appendToBufferWithPrefix)
|
||||
{
|
||||
// Arrange
|
||||
Data::MappedName mappedName(Data::MappedName("TEST"), "POSTFIXTEST");
|
||||
std::string buffer("STUFF");
|
||||
std::string elemMapPrefix = Data::ComplexGeoData::elementMapPrefix();
|
||||
|
||||
// Act
|
||||
mappedName.appendToBufferWithPrefix(buffer);
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(buffer, std::string("STUFF") + elemMapPrefix + std::string("TESTPOSTFIXTEST"));
|
||||
|
||||
// Arrange
|
||||
Data::MappedName mappedName2("TEST"); //If mappedName does not have a postfix and is a valid indexedName: prefix is not added
|
||||
|
||||
// Act
|
||||
mappedName2.appendToBufferWithPrefix(buffer);
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(buffer, std::string("STUFF") + elemMapPrefix + std::string("TESTPOSTFIXTEST") + /*missing prefix*/ std::string("TEST"));
|
||||
}
|
||||
|
||||
TEST(MappedName, toPrefixedString)
|
||||
{
|
||||
// Arrange
|
||||
Data::MappedName mappedName(Data::MappedName("TEST"), "POSTFIXTEST");
|
||||
std::string buffer("STUFF");
|
||||
std::string elemMapPrefix = Data::ComplexGeoData::elementMapPrefix();
|
||||
|
||||
// Act
|
||||
buffer += mappedName.toPrefixedString();
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(buffer, std::string("STUFF") + elemMapPrefix + std::string("TESTPOSTFIXTEST"));
|
||||
|
||||
// Arrange
|
||||
Data::MappedName mappedName2("TEST"); //If mappedName does not have a postfix and is a valid indexedName: prefix is not added
|
||||
|
||||
// Act
|
||||
buffer += mappedName2.toPrefixedString();
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(buffer, std::string("STUFF") + elemMapPrefix + std::string("TESTPOSTFIXTEST") + /*missing prefix*/ std::string("TEST"));
|
||||
}
|
||||
|
||||
TEST(MappedName, toBytes)
|
||||
{
|
||||
// Arrange
|
||||
Data::MappedName mappedName(Data::MappedName("TEST"), "POSTFIXTEST");
|
||||
|
||||
// Act & Assert
|
||||
EXPECT_EQ(mappedName.toBytes(), QByteArray("TESTPOSTFIXTEST"));
|
||||
}
|
||||
|
||||
TEST(MappedName, compare)
|
||||
{
|
||||
// Arrange
|
||||
Data::MappedName mappedName1(Data::MappedName("TEST"), "POSTFIXTEST");
|
||||
Data::MappedName mappedName2(Data::MappedName("TEST"), "POSTFIXTEST");
|
||||
Data::MappedName mappedName3(Data::MappedName("TESTPOST"), "FIXTEST");
|
||||
Data::MappedName mappedName4(Data::MappedName("THIS"), "ISDIFFERENT");
|
||||
Data::MappedName mappedName5(Data::MappedName("SH"), "ORTHER");
|
||||
Data::MappedName mappedName6(Data::MappedName("VERYVERYVERY"), "VERYMUCHLONGER");
|
||||
|
||||
// Act & Assert
|
||||
EXPECT_EQ(mappedName1.compare(mappedName1), 0);
|
||||
EXPECT_EQ(mappedName1.compare(mappedName2), 0);
|
||||
EXPECT_EQ(mappedName1.compare(mappedName3), 0);
|
||||
EXPECT_EQ(mappedName1.compare(mappedName4), -1);
|
||||
EXPECT_EQ(mappedName1.compare(mappedName5), 1);
|
||||
EXPECT_EQ(mappedName1.compare(mappedName6), -1);
|
||||
|
||||
EXPECT_EQ(mappedName1 < mappedName1, false);
|
||||
EXPECT_EQ(mappedName1 < mappedName2, false);
|
||||
EXPECT_EQ(mappedName1 < mappedName3, false);
|
||||
EXPECT_EQ(mappedName1 < mappedName4, true);
|
||||
EXPECT_EQ(mappedName1 < mappedName5, false);
|
||||
EXPECT_EQ(mappedName1 < mappedName6, true);
|
||||
}
|
||||
|
||||
TEST(MappedName, subscriptOperator)
|
||||
{
|
||||
// Arrange
|
||||
Data::MappedName mappedName(Data::MappedName("TEST"), "POSTFIXTEST");
|
||||
|
||||
// Act & Assert
|
||||
EXPECT_EQ(mappedName[0], 'T');
|
||||
EXPECT_EQ(mappedName[1], 'E');
|
||||
EXPECT_EQ(mappedName[2], 'S');
|
||||
EXPECT_EQ(mappedName[3], 'T');
|
||||
EXPECT_EQ(mappedName[4], 'P');
|
||||
EXPECT_EQ(mappedName[5], 'O');
|
||||
EXPECT_EQ(mappedName[6], 'S');
|
||||
EXPECT_EQ(mappedName[7], 'T');
|
||||
EXPECT_EQ(mappedName[8], 'F');
|
||||
EXPECT_EQ(mappedName[9], 'I');
|
||||
}
|
||||
|
||||
TEST(MappedName, copy)
|
||||
{
|
||||
// Arrange
|
||||
Data::MappedName mappedName(Data::MappedName("TEST"), "POSTFIXTEST");
|
||||
|
||||
// Act
|
||||
Data::MappedName mappedName2 = mappedName.copy();
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(mappedName, mappedName2);
|
||||
}
|
||||
|
||||
TEST(MappedName, compact)
|
||||
{
|
||||
// Arrange
|
||||
Data::MappedName mappedName = Data::MappedName::fromRawData("TESTTEST", 10);
|
||||
|
||||
// Act
|
||||
mappedName.compact();
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(mappedName.isRaw(), false);
|
||||
EXPECT_EQ(mappedName.empty(), false);
|
||||
EXPECT_EQ(mappedName.size(), 10);
|
||||
EXPECT_EQ(mappedName.dataBytes(), QByteArray("TESTTEST", 10));
|
||||
EXPECT_EQ(mappedName.postfixBytes(), QByteArray());
|
||||
}
|
||||
|
||||
TEST(MappedName, boolOperator)
|
||||
{
|
||||
// Arrange
|
||||
Data::MappedName mappedName;
|
||||
|
||||
// Act & Assert
|
||||
EXPECT_EQ((bool)mappedName, false);
|
||||
|
||||
// Arrange
|
||||
mappedName.append("TEST");
|
||||
|
||||
// Act & Assert
|
||||
EXPECT_EQ((bool)mappedName, true);
|
||||
}
|
||||
|
||||
TEST(MappedName, clear)
|
||||
{
|
||||
// Arrange
|
||||
Data::MappedName mappedName(Data::MappedName("TEST"), "POSTFIXTEST");
|
||||
|
||||
// Act
|
||||
mappedName.clear();
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(mappedName.empty(), true);
|
||||
}
|
||||
|
||||
TEST(MappedName, find)
|
||||
{
|
||||
// Arrange
|
||||
Data::MappedName mappedName(Data::MappedName("TEST"), "POSTFIXTEST");
|
||||
|
||||
// Act & Assert
|
||||
EXPECT_EQ(mappedName.find(nullptr), -1);
|
||||
EXPECT_EQ(mappedName.find(""), 0);
|
||||
EXPECT_EQ(mappedName.find(std::string("")), 0);
|
||||
EXPECT_EQ(mappedName.find("TEST"), 0);
|
||||
EXPECT_EQ(mappedName.find("STPO"), -1); //sentence must be fully contained in data or postfix
|
||||
EXPECT_EQ(mappedName.find("POST"), 4);
|
||||
EXPECT_EQ(mappedName.find("POST", 4), 4);
|
||||
EXPECT_EQ(mappedName.find("POST", 5), -1);
|
||||
|
||||
EXPECT_EQ(mappedName.rfind("ST"), 13);
|
||||
EXPECT_EQ(mappedName.rfind("ST", 15), 13);
|
||||
EXPECT_EQ(mappedName.rfind("ST", 14), 13);
|
||||
EXPECT_EQ(mappedName.rfind("ST", 13), 13);
|
||||
EXPECT_EQ(mappedName.rfind("ST", 12), 6);
|
||||
EXPECT_EQ(mappedName.rfind("ST", 11), 6);
|
||||
EXPECT_EQ(mappedName.rfind("ST", 10), 6);
|
||||
EXPECT_EQ(mappedName.rfind("ST", 9), 6);
|
||||
EXPECT_EQ(mappedName.rfind("ST", 8), 6);
|
||||
EXPECT_EQ(mappedName.rfind("ST", 7), 6);
|
||||
EXPECT_EQ(mappedName.rfind("ST", 6), 6);
|
||||
EXPECT_EQ(mappedName.rfind("ST", 5), 2);
|
||||
EXPECT_EQ(mappedName.rfind("ST", 4), 2);
|
||||
EXPECT_EQ(mappedName.rfind("ST", 3), 2);
|
||||
EXPECT_EQ(mappedName.rfind("ST", 2), 2);
|
||||
EXPECT_EQ(mappedName.rfind("ST", 1), -1);
|
||||
EXPECT_EQ(mappedName.rfind("ST", 0), -1);
|
||||
}
|
||||
|
||||
TEST(MappedName, rfind)
|
||||
{
|
||||
// Arrange
|
||||
Data::MappedName mappedName(Data::MappedName("TEST"), "POSTFIXTEST");
|
||||
|
||||
// Act & Assert
|
||||
EXPECT_EQ(mappedName.rfind(nullptr), -1);
|
||||
EXPECT_EQ(mappedName.rfind(""), mappedName.size());
|
||||
EXPECT_EQ(mappedName.rfind(std::string("")), mappedName.size());
|
||||
EXPECT_EQ(mappedName.rfind("TEST"), 11);
|
||||
EXPECT_EQ(mappedName.rfind("STPO"), -1); //sentence must be fully contained in data or postfix
|
||||
EXPECT_EQ(mappedName.rfind("POST"), 4);
|
||||
EXPECT_EQ(mappedName.rfind("POST", 4), 4);
|
||||
EXPECT_EQ(mappedName.rfind("POST", 3), -1);
|
||||
|
||||
EXPECT_EQ(mappedName.rfind("ST"), 13);
|
||||
EXPECT_EQ(mappedName.rfind("ST", 0), -1);
|
||||
EXPECT_EQ(mappedName.rfind("ST", 1), -1);
|
||||
EXPECT_EQ(mappedName.rfind("ST", 2), 2);
|
||||
EXPECT_EQ(mappedName.rfind("ST", 3), 2);
|
||||
EXPECT_EQ(mappedName.rfind("ST", 4), 2);
|
||||
EXPECT_EQ(mappedName.rfind("ST", 5), 2);
|
||||
EXPECT_EQ(mappedName.rfind("ST", 6), 6);
|
||||
EXPECT_EQ(mappedName.rfind("ST", 7), 6);
|
||||
EXPECT_EQ(mappedName.rfind("ST", 8), 6);
|
||||
EXPECT_EQ(mappedName.rfind("ST", 9), 6);
|
||||
EXPECT_EQ(mappedName.rfind("ST", 10), 6);
|
||||
EXPECT_EQ(mappedName.rfind("ST", 11), 6);
|
||||
EXPECT_EQ(mappedName.rfind("ST", 12), 6);
|
||||
EXPECT_EQ(mappedName.rfind("ST", 13), 13);
|
||||
EXPECT_EQ(mappedName.rfind("ST", 14), 13);
|
||||
EXPECT_EQ(mappedName.rfind("ST", 15), 13);
|
||||
}
|
||||
|
||||
TEST(MappedName, endswith)
|
||||
{
|
||||
// Arrange
|
||||
Data::MappedName mappedName("TEST");
|
||||
|
||||
// Act & Assert
|
||||
EXPECT_EQ(mappedName.endsWith(nullptr), false);
|
||||
EXPECT_EQ(mappedName.endsWith("TEST"), true);
|
||||
EXPECT_EQ(mappedName.endsWith(std::string("TEST")), true);
|
||||
EXPECT_EQ(mappedName.endsWith("WASD"), false);
|
||||
|
||||
// Arrange
|
||||
mappedName.append("POSTFIX");
|
||||
|
||||
// Act & Assert
|
||||
EXPECT_EQ(mappedName.endsWith(nullptr), false);
|
||||
EXPECT_EQ(mappedName.endsWith("TEST"), false);
|
||||
EXPECT_EQ(mappedName.endsWith("FIX"), true);
|
||||
}
|
||||
|
||||
TEST(MappedName, startsWith)
|
||||
{
|
||||
// Arrange
|
||||
Data::MappedName mappedName;
|
||||
|
||||
// Act & Assert
|
||||
EXPECT_EQ(mappedName.startsWith(nullptr), false);
|
||||
EXPECT_EQ(mappedName.startsWith(QByteArray()), true);
|
||||
EXPECT_EQ(mappedName.startsWith(""), true);
|
||||
EXPECT_EQ(mappedName.startsWith(std::string("")), true);
|
||||
EXPECT_EQ(mappedName.startsWith("WASD"), false);
|
||||
|
||||
// Arrange
|
||||
mappedName.append("TEST");
|
||||
|
||||
// Act & Assert
|
||||
EXPECT_EQ(mappedName.startsWith(nullptr), false);
|
||||
EXPECT_EQ(mappedName.startsWith(QByteArray()), true);
|
||||
EXPECT_EQ(mappedName.startsWith("TEST"), true);
|
||||
EXPECT_EQ(mappedName.startsWith(std::string("TEST")), true);
|
||||
EXPECT_EQ(mappedName.startsWith("WASD"), false);
|
||||
}
|
||||
|
||||
TEST(MappedName, hash)
|
||||
{
|
||||
// Arrange
|
||||
Data::MappedName mappedName(Data::MappedName("TEST"), "POSTFIXTEST");
|
||||
|
||||
// Act & Assert
|
||||
EXPECT_EQ(mappedName.hash(), qHash(QByteArray("TEST"), qHash(QByteArray("POSTFIXTEST"))));
|
||||
}
|
||||
|
||||
// NOLINTEND(readability-magic-numbers)
|
||||
Reference in New Issue
Block a user