init
This commit is contained in:
482
lib/engine/rs_solid.cpp
Normal file
482
lib/engine/rs_solid.cpp
Normal file
@@ -0,0 +1,482 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user