Files
newspark110/lib/engine/rs_solid.cpp
Chenwenxuan edac2715f0 init
2024-03-06 14:54:30 +08:00

483 lines
13 KiB
C++

/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** Copyright (C) 2018 A. Stebich (librecad@mail.lordofbikes.de)
** Copyright (C) 2010 R. van Twisk (librecad@rvt.dds.nl)
** Copyright (C) 2001-2003 RibbonSoft. All rights reserved.
**
**
** This file may be distributed and/or modified under the terms of the
** GNU General Public License version 2 as published by the Free Software
** Foundation and appearing in the file gpl-2.0.txt included in the
** packaging of this 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 General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**
** This copyright notice MUST APPEAR in all copies of the script!
**
**********************************************************************/
#include<iostream>
#include<cmath>
#include "rs_solid.h"
#include "rs_line.h"
#include "rs_graphicview.h"
#include "rs_painter.h"
#include "rs_information.h"
#include "rs_debug.h"
RS_SolidData::RS_SolidData():
corner{{RS_Vector(false), RS_Vector(false), RS_Vector(false), RS_Vector(false)}}
{
}
/**
* Constructor for a solid with 3 corners.
*/
RS_SolidData::RS_SolidData(const RS_Vector& corner1,
const RS_Vector& corner2,
const RS_Vector& corner3):
corner{{corner1, corner2, corner3, RS_Vector(false)}}
{
}
/**
* Constructor for a solid with 4 corners.
*/
RS_SolidData::RS_SolidData(const RS_Vector& corner1,
const RS_Vector& corner2,
const RS_Vector& corner3,
const RS_Vector& corner4):
corner{{corner1, corner2, corner3, corner4}}
{
}
std::ostream& operator << (std::ostream& os,
const RS_SolidData& pd)
{
os << "(";
for (int i = RS_SolidData::FirstCorner; i < RS_SolidData::MaxCorners; i++) {
os << pd.corner[i];
}
os << ")";
return os;
}
/**
* Default constructor.
*/
RS_Solid::RS_Solid(RS_EntityContainer* parent,
const RS_SolidData& d) :
RS_AtomicEntity(parent),
data(d)
{
calculateBorders();
}
RS_Entity* RS_Solid::clone() const
{
RS_Solid* s = new RS_Solid(*this);
s->initId();
return s;
}
/**
* @return Corner number 'num'.
*/
RS_Vector RS_Solid::getCorner(int num) const
{
if (num >= RS_SolidData::FirstCorner && num < RS_SolidData::MaxCorners) {
return data.corner[num];
}
RS_DEBUG->print(RS_Debug::D_WARNING, "Illegal corner requested from Solid");
return RS_Vector(false);
}
/**
* Shapes this Solid into a standard arrow (used in dimensions).
*
* @param point The point the arrow points to.
* @param angle Direction of the arrow.
* @param arrowSize Size of arrow (length).
*/
void RS_Solid::shapeArrow(const RS_Vector& point,
double angle,
double arrowSize)
{
double arrowSide {arrowSize / cos(0.165)};
double cosv1 {cos( angle + 0.165) * arrowSide};
double sinv1 {sin( angle + 0.165) * arrowSide};
double cosv2 {cos( angle - 0.165) * arrowSide};
double sinv2 {sin( angle - 0.165) * arrowSide};
data.corner[0] = point;
data.corner[1] = RS_Vector(point.x - cosv1, point.y - sinv1);
data.corner[2] = RS_Vector(point.x - cosv2, point.y - sinv2);
data.corner[3] = RS_Vector(false);
calculateBorders();
}
void RS_Solid::calculateBorders()
{
resetBorders();
for (int i = RS_SolidData::FirstCorner; i < RS_SolidData::MaxCorners; ++i) {
if (data.corner[i].valid) {
minV = RS_Vector::minimum( minV, data.corner[i]);
maxV = RS_Vector::maximum( maxV, data.corner[i]);
}
}
}
RS_Vector RS_Solid::getNearestEndpoint(const RS_Vector& coord, double* dist /*= nullptr*/)const
{
double minDist {RS_MAXDOUBLE};
double curDist {0.0};
RS_Vector ret;
for (int i = RS_SolidData::FirstCorner; i < RS_SolidData::MaxCorners; ++i) {
if (data.corner[i].valid) {
curDist = data.corner[i].distanceTo(coord);
if (curDist < minDist) {
ret = data.corner[i];
minDist = curDist;
}
}
}
setDistPtr( dist, minDist);
return ret;
}
bool RS_Solid::isInCrossWindow(const RS_Vector& v1, const RS_Vector& v2) const
{
RS_Vector vBL;
RS_Vector vTR;
RS_VectorSolutions sol;
//sort input vectors to BottomLeft & TopRight
if (v1.x < v2.x) {
vBL.x = v1.x;
vTR.x = v2.x;
}
else {
vBL.x = v2.x;
vTR.x = v1.x;
}
if (v1.y < v2.y) {
vBL.y = v1.y;
vTR.y = v2.y;
}
else {
vBL.y = v2.y;
vTR.y = v1.y;
}
//Check if entity is out of window
if (getMin().x > vTR.x
|| getMax().x < vBL.x
|| getMin().y > vTR.y
|| getMax().y < vBL.y) {
return false;
}
std::vector<RS_Line> border;
border.emplace_back( data.corner[0], data.corner[1]);
border.emplace_back( data.corner[1], data.corner[2]);
if (isTriangle()) {
border.emplace_back( data.corner[2], data.corner[0]);
}
else {
border.emplace_back( data.corner[2], data.corner[3]);
border.emplace_back( data.corner[3], data.corner[0]);
}
//Find crossing edge
if (getMax().x > vBL.x
&& getMin().x < vBL.x) { //left
RS_Line edge {vBL, {vBL.x, vTR.y}};
for (auto const& line: border) {
sol = RS_Information::getIntersection(&edge, &line, true);
if (sol.hasValid()) {
return true;
}
}
}
if (getMax().x > vTR.x
&& getMin().x < vTR.x) { //right
RS_Line edge {{vTR.x, vBL.y}, vTR};
for (auto const& line: border) {
sol = RS_Information::getIntersection(&edge, &line, true);
if (sol.hasValid()) {
return true;
}
}
}
if (getMax().y > vBL.y
&& getMin().y < vBL.y) { //bottom
RS_Line edge {vBL, {vTR.x, vBL.y}};
for(auto const& line: border) {
sol = RS_Information::getIntersection(&edge, &line, true);
if (sol.hasValid()) {
return true;
}
}
}
if(getMax().y > vTR.y
&& getMin().y < vTR.y) { //top
RS_Line edge {{vBL.x, vTR.y}, vTR};
for(auto const& line: border) {
sol = RS_Information::getIntersection(&edge, &line, true);
if (sol.hasValid()) {
return true;
}
}
}
return false;
}
/**
*
* @return true if positive o zero, false if negative.
*/
bool RS_Solid::sign (const RS_Vector& v1, const RS_Vector& v2, const RS_Vector& v3) const
{
double res = (v1.x - v3.x) * (v2.y - v3.y) - (v2.x - v3.x) * (v1.y - v3.y);
return (res >= 0.0);
}
/**
* @todo Implement this.
*/
RS_Vector RS_Solid::getNearestPointOnEntity(const RS_Vector& coord,
bool onEntity /*= true*/,
double* dist /*= nullptr*/,
RS_Entity** entity /*= nullptr*/) const
{
//first check if point is inside solid
bool s1 {sign(data.corner[0], data.corner[1], coord)};
bool s2 {sign(data.corner[1], data.corner[2], coord)};
bool s3 {sign(data.corner[2], data.corner[0], coord)};
if ((s1 == s2) && (s2 == s3)) {
setDistPtr( dist, 0.0);
return coord;
}
if (!isTriangle()) {
s1 = sign(data.corner[0], data.corner[2], coord);
s2 = sign(data.corner[2], data.corner[3], coord);
s3 = sign(data.corner[3], data.corner[0], coord);
if ((s1 == s2) && (s2 == s3)) {
setDistPtr( dist, 0.0);
return coord;
}
}
// not inside of solid
// Find nearest distance from each edge
if (nullptr != entity) {
*entity = const_cast<RS_Solid*>(this);
}
RS_Vector ret(false);
double currDist {RS_MAXDOUBLE};
double tmpDist {0.0};
int totalV {isTriangle() ? RS_SolidData::Triangle : RS_SolidData::MaxCorners};
for (int i = RS_SolidData::FirstCorner, next = i + 1; i <= totalV; ++i, ++next) {
//closing edge
if (next == totalV) {
next = RS_SolidData::FirstCorner;
}
RS_Vector direction {data.corner[next] - data.corner[i]};
RS_Vector vpc {coord-data.corner[i]};
double a {direction.squared()};
if( a < RS_TOLERANCE2) {
//line too short
vpc = data.corner[i];
}
else{
//find projection on line
vpc = data.corner[i] + direction * RS_Vector::dotP( vpc, direction) / a;
}
tmpDist = vpc.distanceTo( coord);
if (tmpDist < currDist) {
currDist = tmpDist;
ret = vpc;
}
}
//verify this part
if (onEntity && !ret.isInWindowOrdered( minV, maxV)) {
// projection point not within range, find the nearest endpoint
ret = getNearestEndpoint( coord, dist);
currDist = ret.distanceTo( coord);
}
setDistPtr( dist, currDist);
return ret;
}
RS_Vector RS_Solid::getNearestCenter(const RS_Vector& coord,
double* dist /*= nullptr*/) const
{
Q_UNUSED( coord)
setDistPtr( dist, RS_MAXDOUBLE);
return RS_Vector(false);
}
RS_Vector RS_Solid::getNearestMiddle(const RS_Vector& coord,
double* dist /*= nullptr*/,
const int middlePoints /*= 1*/) const
{
Q_UNUSED( coord)
Q_UNUSED( middlePoints)
setDistPtr( dist, RS_MAXDOUBLE);
return RS_Vector(false);
}
RS_Vector RS_Solid::getNearestDist(double distance,
const RS_Vector& coord,
double* dist /*= nullptr*/) const
{
Q_UNUSED( distance)
Q_UNUSED( coord)
setDistPtr( dist, RS_MAXDOUBLE);
return RS_Vector(false);
}
/**
* @return Distance from one of the boundary lines of this solid to given point.
*
*/
double RS_Solid::getDistanceToPoint(const RS_Vector& coord,
RS_Entity** entity /*= nullptr*/,
RS2::ResolveLevel level /*= RS2::ResolveNone*/,
double solidDist /*= RS_MAXDOUBLE*/) const
{
Q_UNUSED( level)
Q_UNUSED( solidDist)
if (nullptr != entity) {
*entity = const_cast<RS_Solid*>(this);
}
double ret {0.0};
getNearestPointOnEntity( coord, true, &ret, entity);
return ret;
}
void RS_Solid::move(const RS_Vector& offset)
{
for (int i = RS_SolidData::FirstCorner; i < RS_SolidData::MaxCorners; ++i) {
if (data.corner[i].valid) {
data.corner[i].move(offset);
}
}
calculateBorders();
}
void RS_Solid::rotate(const RS_Vector& center, const double& angle)
{
RS_Vector angleVector(angle);
for (int i = RS_SolidData::FirstCorner; i < RS_SolidData::MaxCorners; ++i) {
if (data.corner[i].valid) {
data.corner[i].rotate( center, angleVector);
}
}
calculateBorders();
}
void RS_Solid::rotate(const RS_Vector& center, const RS_Vector& angleVector)
{
for (int i = RS_SolidData::FirstCorner; i < RS_SolidData::MaxCorners; ++i) {
if (data.corner[i].valid) {
data.corner[i].rotate( center, angleVector);
}
}
calculateBorders();
}
void RS_Solid::scale(const RS_Vector& center, const RS_Vector& factor)
{
for (int i = RS_SolidData::FirstCorner; i < RS_SolidData::MaxCorners; ++i) {
if (data.corner[i].valid) {
data.corner[i].scale( center, factor);
}
}
calculateBorders();
}
void RS_Solid::mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2)
{
for (int i = RS_SolidData::FirstCorner; i < RS_SolidData::MaxCorners; ++i) {
if (data.corner[i].valid) {
data.corner[i].mirror( axisPoint1, axisPoint2);
}
}
calculateBorders();
}
void RS_Solid::draw(RS_Painter* painter,
RS_GraphicView* view,
double& patternOffset)
{
Q_UNUSED( patternOffset)
if (nullptr == painter
|| nullptr == view) {
return;
}
painter->fillTriangle( view->toGui(getCorner(0)),
view->toGui(getCorner(1)),
view->toGui(getCorner(2)));
if (!isTriangle()) {
painter->fillTriangle( view->toGui(getCorner(1)),
view->toGui(getCorner(2)),
view->toGui(getCorner(3)));
}
}
void RS_Solid::setDistPtr(double *dist, const double value) const
{
if (nullptr != dist) {
*dist = value;
}
}
/**
* Dumps the point's data to stdout.
*/
std::ostream& operator << (std::ostream& os, const RS_Solid& p)
{
os << " Solid: " << p.getData() << "\n";
return os;
}