/** @return the nearest of equidistant middle points of the line. */ RS_Vector RS_Line::getNearestMiddle(const RS_Vector& coord, double* dist, int middlePoints )const { // RS_DEBUG->print("RS_Line::getNearestMiddle(): begin\n"); RS_Vector dvp(getEndpoint() - getStartpoint()); double l=dvp.magnitude(); if( l<= RS_TOLERANCE) { //line too short return const_cast<RS_Line*>(this)->getNearestCenter(coord, dist); /* ?????? */ } RS_Vector vp0(getNearestPointOnEntity(coord,true,dist)); int counts=middlePoints+1; int i( static_cast<int>(vp0.distanceTo(getStartpoint())/l*counts+0.5)); if(!i) i++; // remove end points if(i==counts) i--; vp0=getStartpoint() + dvp*(double(i)/double(counts)); if(dist != NULL) { *dist=vp0.distanceTo(coord); } //std::cout << "rs_line.cpp Dist " << *dist << "\n"; // RS_DEBUG->print("RS_Line::getNearestMiddle(): end\n"); return vp0; }
RS_Vector RS_Line::prepareTrim(const RS_Vector& trimCoord, const RS_VectorSolutions& trimSol) { //prepare trimming for multiple intersections if ( ! trimSol.hasValid()) return(RS_Vector(false)); if ( trimSol.getNumber() == 1 ) return(trimSol.get(0)); auto&& vp0=trimSol.getClosest(trimCoord,NULL,0); double dr2=trimCoord.squaredTo(vp0); //the trim point found is closer to mouse location (trimCoord) than both end points, return this trim point if(dr2 < trimCoord.squaredTo(getStartpoint()) && dr2 < trimCoord.squaredTo(getEndpoint())) return vp0; //the closer endpoint to trimCoord RS_Vector vp1=(trimCoord.squaredTo(getStartpoint()) <= trimCoord.squaredTo(getEndpoint()))?getStartpoint():getEndpoint(); //searching for intersection in the direction of the closer end point auto&& dvp1=vp1 - trimCoord; RS_VectorSolutions sol1; for(size_t i=0; i<trimSol.size(); i++){ auto&& dvp2=trimSol.at(i) - trimCoord; if( RS_Vector::dotP(dvp1, dvp2) > RS_TOLERANCE) sol1.push_back(trimSol.at(i)); } //if found intersection in direction, return the closest to trimCoord from it if(sol1.size()) return sol1.getClosest(trimCoord,NULL,0); //no intersection by direction, return previously found closest intersection return vp0; }
/** @return the nearest of equidistant middle points of the line. */ RS_Vector RS_Line::getNearestMiddle(const RS_Vector& coord, double* dist, int middlePoints ) { RS_DEBUG->print("RS_Line::getNearestMiddle(): begin\n"); RS_Vector dvp(getEndpoint() - getStartpoint()); double l=dvp.magnitude(); if( l<= RS_TOLERANCE) { //line too short RS_Vector vp(getStartpoint() + dvp*0.5); if (dist != NULL) { *dist=vp.distanceTo(coord); } return vp; } RS_Vector vp0(getNearestPointOnEntity(coord,true,dist)); int counts=middlePoints+1; int i( static_cast<int>(vp0.distanceTo(getStartpoint())/l*counts+0.5)); if(!i) i++; // remove end points if(i==counts) i--; vp0=getStartpoint() + dvp*(double(i)/double(counts)); if(dist != NULL) { *dist=vp0.distanceTo(coord); } RS_DEBUG->print("RS_Line::getNearestMiddle(): end\n"); return vp0; }
/** * Stretches the given range of the entity by the given offset. */ void RS_Line::stretch(RS_Vector firstCorner, RS_Vector secondCorner, RS_Vector offset) { if (getStartpoint().isInWindow(firstCorner, secondCorner)) { moveStartpoint(getStartpoint() + offset); } if (getEndpoint().isInWindow(firstCorner, secondCorner)) { moveEndpoint(getEndpoint() + offset); } }
/** * Stretches the given range of the entity by the given offset. */ void RS_Line::stretch(const RS_Vector& firstCorner, const RS_Vector& secondCorner, const RS_Vector& offset) { RS_Vector vLow( std::min(firstCorner.x, secondCorner.x), std::min(firstCorner.y, secondCorner.y)); RS_Vector vHigh( std::max(firstCorner.x, secondCorner.x), std::max(firstCorner.y, secondCorner.y)); if (getStartpoint().isInWindowOrdered(vLow, vHigh)) { moveStartpoint(getStartpoint() + offset); } if (getEndpoint().isInWindowOrdered(vLow, vHigh)) { moveEndpoint(getEndpoint() + offset); } }
/** * this function creates offset *@coord, position indicates the direction of offset *@distance, distance of offset * return true, if success, otherwise, false * *Author: Dongxu Li */ bool RS_Line::offset(const RS_Vector& coord, const double& distance) { RS_Vector direction{getEndpoint()-getStartpoint()}; double ds(direction.magnitude()); if(ds< RS_TOLERANCE) return false; direction /= ds; RS_Vector vp(coord-getStartpoint()); // RS_Vector vp1(getStartpoint() + direction*(RS_Vector::dotP(direction,vp))); //projection direction.set(-direction.y,direction.x); //rotate pi/2 if(RS_Vector::dotP(direction,vp)<0.) { direction *= -1.; } direction*=distance; move(direction); return true; }
double RS_Arc::getDistanceToPoint(const RS_Vector& coord, RS_Entity** entity, RS2::ResolveLevel, double) { if (entity!=NULL) { *entity = this; } // check endpoints first: double dist = coord.distanceTo(getStartpoint()); if (dist<1.0e-4) { return dist; } dist = coord.distanceTo(getEndpoint()); if (dist<1.0e-4) { return dist; } if (RS_Math::isAngleBetween(data.center.angleTo(coord), data.angle1, data.angle2, isReversed())) { return fabs((coord-data.center).magnitude() - data.radius); } else { return RS_MAXDOUBLE; } }
RS_Vector RS_Spline::getNearestEndpoint(const RS_Vector& coord, double* dist)const { double minDist = RS_MAXDOUBLE; RS_Vector ret(false); if(! data.closed) { // no endpoint for closed spline RS_Vector vp1(getStartpoint()); RS_Vector vp2(getEndpoint()); double d1( (coord-vp1).squared()); double d2( (coord-vp2).squared()); if( d1<d2){ ret=vp1; minDist=sqrt(d1); }else{ ret=vp2; minDist=sqrt(d2); } // for (int i=0; i<data.controlPoints.count(); i++) { // d = (data.controlPoints.at(i)).distanceTo(coord); // if (d<minDist) { // minDist = d; // ret = data.controlPoints.at(i); // } // } } if (dist!=nullptr) { *dist = minDist; } return ret; }
/* Dongxu Li's Version, 19 Aug 2011 * scale an ellipse * Find the eigen vactors and eigen values by optimization * original ellipse equation, * x= a cos t * y= b sin t * rotated by angle, * * x = a cos t cos (angle) - b sin t sin(angle) * y = a cos t sin (angle) + b sin t cos(angle) * scaled by ( kx, ky), * x *= kx * y *= ky * find the maximum and minimum of x^2 + y^2, */ void RS_Ellipse::scale(RS_Vector center, RS_Vector factor) { data.center.scale(center, factor); RS_Vector vpStart=getStartpoint().scale(getCenter(),factor); RS_Vector vpEnd=getEndpoint().scale(getCenter(),factor);; double ct=cos(getAngle()); double ct2 = ct*ct; // cos^2 angle double st=sin(getAngle()); double st2=1.0 - ct2; // sin^2 angle double kx2= factor.x * factor.x; double ky2= factor.y * factor.y; double a=getMajorRadius(); double b=getMinorRadius(); double cA=0.5*a*a*(kx2*ct2+ky2*st2); double cB=0.5*b*b*(kx2*st2+ky2*ct2); double cC=a*b*ct*st*(ky2-kx2); RS_Vector vp(cA-cB,cC); setMajorP(RS_Vector(a,b).scale(RS_Vector(vp.angle())).rotate(RS_Vector(ct,st)).scale(factor)); a=cA+cB; b=vp.magnitude(); setRatio( sqrt((a - b)/(a + b) )); if( std::isnormal(getAngle1()) || std::isnormal(getAngle2() ) ) { //only reset start/end points for ellipse arcs, i.e., angle1 angle2 are not both zero setAngle1(getEllipseAngle(vpStart)); setAngle2(getEllipseAngle(vpEnd)); } correctAngles();//avoid extra 2.*M_PI in angles //calculateEndpoints(); calculateBorders(); }
bool RS_Ellipse::switchMajorMinor(void) //switch naming of major/minor, return true if success { if (fabs(data.ratio) < RS_TOLERANCE) return false; RS_Vector vp_start=getStartpoint(); RS_Vector vp_end=getStartpoint(); RS_Vector vp=getMajorP(); setMajorP(RS_Vector(- data.ratio*vp.y, data.ratio*vp.x)); //direction pi/2 relative to old MajorP; setRatio(1./data.ratio); if( std::isnormal(getAngle1()) || std::isnormal(getAngle2() ) ) { //only reset start/end points for ellipse arcs, i.e., angle1 angle2 are not both zero setAngle1(getEllipseAngle(vp_start)); setAngle2(getEllipseAngle(vp_end)); } return true; }
RS_Vector RS_Ellipse::getNearestMiddle(const RS_Vector& coord, double* dist, int middlePoints ) { if ( ! ( std::isnormal(getAngle1()) || std::isnormal(getAngle2()))) { //no middle point for whole ellipse, angle1=angle2=0 if (dist!=NULL) { *dist = RS_MAXDOUBLE; } return RS_Vector(false); } if ( getMajorRadius() < RS_TOLERANCE || getMinorRadius() < RS_TOLERANCE ) { //zero radius, return the center RS_Vector vp(getCenter()); if (dist!=NULL) { *dist = vp.distanceTo(coord); } return vp; } double angle=getAngle(); double amin=getCenter().angleTo(getStartpoint()); double amax=getCenter().angleTo(getEndpoint()); if(isReversed()) { std::swap(amin,amax); } int i=middlePoints + 1; double da=fmod(amax-amin+2.*M_PI, 2.*M_PI); if ( da < RS_TOLERANCE ) { da = 2.*M_PI; //whole ellipse } da /= i; int j=1; double curDist=RS_MAXDOUBLE; //double a=RS_Math::correctAngle(amin+da-angle); double a=amin-angle+da; RS_Vector curPoint; RS_Vector scaleFactor(RS_Vector(1./getMajorRadius(),1./getMinorRadius())); do { RS_Vector vp(a); RS_Vector vp2=vp; vp2.scale(scaleFactor); vp.scale(1./vp2.magnitude()); vp.rotate(angle); vp.move(getCenter()); double d=coord.distanceTo(vp); if(d<curDist) { curDist=d; curPoint=vp; } j++; a += da; } while (j<i); if (dist!=NULL) { *dist = curDist; } RS_DEBUG->print("RS_Ellipse::getNearestMiddle: angle1=%g, angle2=%g, middle=%g\n",amin,amax,a); return curPoint; }
RS_Vector RS_Line::getNearestDist(double distance, const RS_Vector& coord, double* dist) const{ RS_Vector dv = RS_Vector::polar(distance, getAngle1()); RS_Vector ret; //if(coord.distanceTo(getStartpoint()) < coord.distanceTo(getEndpoint())) { if( (coord-getStartpoint()).squared()< (coord-getEndpoint()).squared() ) { ret = getStartpoint() + dv; }else{ ret = getEndpoint() - dv; } if (dist) *dist=coord.distanceTo(ret); return ret; }
void RS_OverlayLine::draw(RS_Painter* painter, RS_GraphicView* view, double patternOffset) { if (painter==NULL || view==NULL) { return; } painter->drawLine(getStartpoint(), getEndpoint()); }
RS2::Ending RS_Line::getTrimPoint(const RS_Vector& trimCoord, const RS_Vector& trimPoint) { RS_Vector vp1=getStartpoint() - trimCoord; RS_Vector vp2=trimPoint - trimCoord; if ( RS_Vector::dotP(vp1,vp2) < 0 ) { return RS2::EndingEnd; } else { return RS2::EndingStart; } }
void RS_Arc::stretch(RS_Vector firstCorner, RS_Vector secondCorner, RS_Vector offset) { if (getMin().isInWindow(firstCorner, secondCorner) && getMax().isInWindow(firstCorner, secondCorner)) { move(offset); } else { if (getStartpoint().isInWindow(firstCorner, secondCorner)) { moveStartpoint(getStartpoint() + offset); } if (getEndpoint().isInWindow(firstCorner, secondCorner)) { moveEndpoint(getEndpoint() + offset); } } }
void RS_Ellipse::moveRef(const RS_Vector& ref, const RS_Vector& offset) { RS_Vector startpoint = getStartpoint(); RS_Vector endpoint = getEndpoint(); if (ref.distanceTo(startpoint)<1.0e-4) { moveStartpoint(startpoint+offset); } if (ref.distanceTo(endpoint)<1.0e-4) { moveEndpoint(endpoint+offset); } }
void RS_Ellipse::moveRef(const RS_Vector& ref, const RS_Vector& offset) { RS_Vector startpoint = getStartpoint(); RS_Vector endpoint = getEndpoint(); if (ref.distanceTo(startpoint)<1.0e-4) { moveStartpoint(startpoint+offset); } if (ref.distanceTo(endpoint)<1.0e-4) { moveEndpoint(endpoint+offset); } correctAngles();//avoid extra 2.*M_PI in angles }
void RS_Arc::moveEndpoint(const RS_Vector& pos) { // polyline arcs: move point not angle: //if (parent!=NULL && parent->rtti()==RS2::EntityPolyline) { double bulge = getBulge(); createFrom2PBulge(getStartpoint(), pos, bulge); //} // normal arc: move angle1 /*else { data.angle2 = data.center.angleTo(pos); calculateEndpoints(); calculateBorders(); }*/ }
/** whether the entity's bounding box intersects with visible portion of graphic view */ bool RS_Entity::isVisibleInWindow(RS_GraphicView* view) const { RS_Vector vpMin(view->toGraph(0,view->getHeight())); RS_Vector vpMax(view->toGraph(view->getWidth(),0)); if( getStartpoint().isInWindowOrdered(vpMin, vpMax) ) return true; if( getEndpoint().isInWindowOrdered(vpMin, vpMax) ) return true; QPolygonF visualBox(QRectF(vpMin.x,vpMin.y,vpMax.x-vpMin.x, vpMax.y-vpMin.y)); std::vector<RS_Vector> vps; for(unsigned short i=0;i<4;i++){ const QPointF& vp(visualBox.at(i)); vps.emplace_back(vp.x(),vp.y()); } for(unsigned short i=0;i<4;i++){ RS_Line const line{vps.at(i),vps.at((i+1)%4)}; if( RS_Information::getIntersection(this, &line, true).size()>0) return true; } if( minV.isInWindowOrdered(vpMin,vpMax)||maxV.isInWindowOrdered(vpMin,vpMax)) return true; return false; }
/** * mirror by the axis defined by axisPoint1 and axisPoint2 */ void RS_Ellipse::mirror(RS_Vector axisPoint1, RS_Vector axisPoint2) { RS_Vector center=getCenter(); RS_Vector mp = center + getMajorP(); RS_Vector startpoint = getStartpoint(); RS_Vector endpoint = getEndpoint(); center.mirror(axisPoint1, axisPoint2); mp.mirror(axisPoint1, axisPoint2); startpoint.mirror(axisPoint1, axisPoint2); endpoint.mirror(axisPoint1, axisPoint2); setCenter(center); setReversed(!isReversed()); setMajorP(mp - center); if( std::isnormal(getAngle1()) || std::isnormal(getAngle2() ) ) { //only reset start/end points for ellipse arcs, i.e., angle1 angle2 are not both zero setAngle1( getEllipseAngle(startpoint)); setAngle2( getEllipseAngle(endpoint)); } /* old version data.majorP = mp - data.center; double a = axisPoint1.angleTo(axisPoint2); RS_Vector vec; vec.setPolar(1.0, data.angle1); vec.mirror(RS_Vector(0.0,0.0), axisPoint2-axisPoint1); data.angle1 = vec.angle() - 2*a; vec.setPolar(1.0, data.angle2); vec.mirror(RS_Vector(0.0,0.0), axisPoint2-axisPoint1); data.angle2 = vec.angle() - 2*a; data.reversed = (!data.reversed); */ //calculateEndpoints(); correctAngles();//avoid extra 2.*M_PI in angles calculateBorders(); }
/** * Calculates the boundary box of this ellipse. * * @todo Fix that - the algorithm used is really bad / slow. */ void RS_Ellipse::calculateBorders() { RS_DEBUG->print("RS_Ellipse::calculateBorders"); double radius1 = getMajorRadius(); double radius2 = getMinorRadius(); double angle = getAngle(); double a1 = ((!isReversed()) ? data.angle1 : data.angle2); double a2 = ((!isReversed()) ? data.angle2 : data.angle1); RS_Vector startpoint = getStartpoint(); RS_Vector endpoint = getEndpoint(); double minX = std::min(startpoint.x, endpoint.x); double minY = std::min(startpoint.y, endpoint.y); double maxX = std::max(startpoint.x, endpoint.x); double maxY = std::max(startpoint.y, endpoint.y); // kind of a brute force. TODO: calculation RS_Vector vp; double a = a1; do { vp.set(data.center.x + radius1 * cos(a), data.center.y + radius2 * sin(a)); vp.rotate(data.center, angle); minX = std::min(minX, vp.x); minY = std::min(minY, vp.y); maxX = std::max(maxX, vp.x); maxY = std::max(maxY, vp.y); a += 0.03; } while (RS_Math::isAngleBetween(RS_Math::correctAngle(a), a1, a2, false) && a<4*M_PI); minV.set(minX, minY); maxV.set(maxX, maxY); RS_DEBUG->print("RS_Ellipse::calculateBorders: OK"); }
RS_Vector RS_Ellipse::getNearestEndpoint(const RS_Vector& coord, double* dist) { double dist1, dist2; RS_Vector nearerPoint; RS_Vector startpoint = getStartpoint(); RS_Vector endpoint = getEndpoint(); dist1 = startpoint.distanceTo(coord); dist2 = endpoint.distanceTo(coord); if (dist2<dist1) { if (dist!=NULL) { *dist = dist2; } nearerPoint = endpoint; } else { if (dist!=NULL) { *dist = dist1; } nearerPoint = startpoint; } return nearerPoint; }
RS_Vector RS_Line::getMiddlePoint()const { return (getStartpoint() + getEndpoint())*0.5; }
void RS_Line::draw(RS_Painter* painter, RS_GraphicView* view, double patternOffset) { if (painter==NULL || view==NULL) { return; } if (getPen().getLineType()==RS2::SolidLine || isSelected() || view->getDrawingMode()==RS2::ModePreview) { painter->drawLine(view->toGui(getStartpoint()), view->toGui(getEndpoint())); } else { double styleFactor = getStyleFactor(view); if (styleFactor<0.0) { painter->drawLine(view->toGui(getStartpoint()), view->toGui(getEndpoint())); return; } // Pattern: RS_LineTypePattern* pat; if (isSelected()) { pat = &patternSelected; } else { pat = view->getPattern(getPen().getLineType()); } if (pat==NULL) { RS_DEBUG->print(RS_Debug::D_WARNING, "RS_Line::draw: Invalid line pattern"); return; } // Pen to draw pattern is always solid: RS_Pen pen = painter->getPen(); pen.setLineType(RS2::SolidLine); painter->setPen(pen); // index counter int i; // line data: double length = getLength(); double angle = getAngle1(); // pattern segment length: double patternSegmentLength = 0.0; // create pattern: RS_Vector* dp = new RS_Vector[pat->num]; for (i=0; i<pat->num; ++i) { dp[i] = RS_Vector(cos(angle) * fabs(pat->pattern[i] * styleFactor), sin(angle) * fabs(pat->pattern[i] * styleFactor)); patternSegmentLength += fabs(pat->pattern[i] * styleFactor); } // handle pattern offset: int m; if (patternOffset<0.0) { m = (int)ceil(patternOffset / patternSegmentLength); } else { m = (int)floor(patternOffset / patternSegmentLength); } patternOffset -= (m*patternSegmentLength); //if (patternOffset<0.0) { // patternOffset+=patternSegmentLength; //} //RS_DEBUG->print("pattern. offset: %f", patternOffset); RS_Vector patternOffsetVec; patternOffsetVec.setPolar(patternOffset, angle); double tot=patternOffset; i=0; bool done = false; bool cutStartpoint, cutEndpoint, drop; RS_Vector curP=getStartpoint()+patternOffsetVec; do { cutStartpoint = false; cutEndpoint = false; drop = false; // line segment (otherwise space segment) if (pat->pattern[i]>0.0) { // drop the whole pattern segment line: if (tot+pat->pattern[i]*styleFactor < 0.0) { drop = true; } else { // trim startpoint of pattern segment line to line startpoint if (tot < 0.0) { cutStartpoint = true; } // trim endpoint of pattern segment line to line endpoint if (tot+pat->pattern[i]*styleFactor > length) { cutEndpoint = true; } } if (drop) { // do nothing } else { RS_Vector p1 = curP; RS_Vector p2 = curP + dp[i]; if (cutStartpoint) { p1 = getStartpoint(); } if (cutEndpoint) { p2 = getEndpoint(); } painter->drawLine(view->toGui(p1), view->toGui(p2)); } } curP+=dp[i]; tot+=fabs(pat->pattern[i]*styleFactor); //RS_DEBUG->print("pattern. tot: %f", tot); done=tot>length; i++; if (i>=pat->num) { i=0; } } while(!done); delete[] dp; } }
/** * @todo Implement this. */ RS_Vector RS_Ellipse::getNearestPointOnEntity(const RS_Vector& coord, bool onEntity, double* dist, RS_Entity** entity) { RS_DEBUG->print("RS_Ellipse::getNearestPointOnEntity"); RS_Vector ret(false); if (entity!=NULL) { *entity = this; } double step; double a1; double a2; double d; double minDist = RS_MAXDOUBLE; if (!onEntity) { a1 = 0.0; a2 = 2*M_PI; } else { if (!data.reversed) { a1 = data.angle1; a2 = data.angle2; } else { a1 = data.angle2; a2 = data.angle1; } } // find closest point (approximate) RS_Vector vp; double bestMatch = 0.0; step = 0.01; double a = a1+step; int eternal = 0; do { // point on elllipse (wanders from a1 to a2) vp.set(data.center.x+cos(a)*getMajorRadius(), data.center.y+sin(a)*getMinorRadius()); vp.rotate(data.center, getAngle()); d = vp.distanceTo(coord); if (d<minDist) { minDist = d; bestMatch = a; ret = vp; } a += step; eternal++; } while (eternal<10000 && RS_Math::isAngleBetween(RS_Math::correctAngle(a), a1, a2, false) && (onEntity || a<2*M_PI)); // correct: /* a = bestMatch; step = 0.03; do { vp.set(data.center.x+cos(a)*getMajorRadius(), data.center.y+sin(a)*getMinorRadius()); vp.rotate(data.center, getAngle()); d = vp.distanceTo(coord); if (d<minDist) { minDist = d; bestMatch = a; ret = vp; } else { // change direction and decrease step amount step*=-0.3; } a += step; } while (fabs(step)>1.0e-6); */ // check endpoints: if (onEntity) { d = getStartpoint().distanceTo(coord); if (d<minDist) { ret = getStartpoint(); } d = getEndpoint().distanceTo(coord); if (d<minDist) { ret = getEndpoint(); } } if (dist!=NULL) { if (ret.valid) { *dist = ret.distanceTo(coord); } else { *dist = RS_MAXDOUBLE; } } RS_DEBUG->print("RS_Ellipse::getNearestPointOnEntity: OK"); return ret; }
RS_VectorSolutions RS_Ellipse::getRefPoints() { RS_VectorSolutions ret(getStartpoint(), getEndpoint(), data.center); return ret; }
/** * Calculates the boundary box of this ellipse. */ void RS_Ellipse::calculateBorders() { RS_DEBUG->print("RS_Ellipse::calculateBorders"); double radius1 = getMajorRadius(); double radius2 = getMinorRadius(); double angle = getAngle(); //double a1 = ((!isReversed()) ? data.angle1 : data.angle2); //double a2 = ((!isReversed()) ? data.angle2 : data.angle1); RS_Vector startpoint = getStartpoint(); RS_Vector endpoint = getEndpoint(); double minX = std::min(startpoint.x, endpoint.x); double minY = std::min(startpoint.y, endpoint.y); double maxX = std::max(startpoint.x, endpoint.x); double maxY = std::max(startpoint.y, endpoint.y); RS_Vector vp; // kind of a brute force. TODO: exact calculation // double a = a1; // do { // vp.set(data.center.x + radius1 * cos(a), // data.center.y + radius2 * sin(a)); // vp.rotate(data.center, angle); // // minX = std::min(minX, vp.x); // minY = std::min(minY, vp.y); // maxX = std::max(maxX, vp.x); // maxY = std::max(maxY, vp.y); // // a += 0.03; // } while (RS_Math::isAngleBetween(RS_Math::correctAngle(a), a1, a2, false) && // a<4*M_PI); // std::cout<<"a1="<<a1<<"\ta2="<<a2<<std::endl<<"Old algorithm:\nminX="<<minX<<"\tmaxX="<<maxX<<"\nminY="<<minY<<"\tmaxY="<<maxY<<std::endl; // Exact algorithm, based on rotation: // ( r1*cos(a), r2*sin(a)) rotated by angle to // (r1*cos(a)*cos(angle)-r2*sin(a)*sin(angle),r1*cos(a)*sin(angle)+r2*sin(a)*cos(angle)) // both coordinates can be further reorganized to the form rr*cos(a+ theta), // with rr and theta angle defined by the coordinates given above double amin,amax; // x range vp.set(radius1*cos(angle),radius2*sin(angle)); amin=RS_Math::correctAngle(getAngle1()+vp.angle()); // to the range of 0 to 2*M_PI amax=RS_Math::correctAngle(getAngle2()+vp.angle()); // to the range of 0 to 2*M_PI if( RS_Math::isAngleBetween(M_PI,amin,amax,isReversed()) ) { //if( (amin<=M_PI && delta_a >= M_PI - amin) || (amin > M_PI && delta_a >= 3*M_PI - amin)) { minX= data.center.x-vp.magnitude(); } // else // minX=data.center.x +vp.magnitude()*std::min(cos(amin),cos(amin+delta_a)); if( RS_Math::isAngleBetween(2.*M_PI,amin,amax,isReversed()) ) { //if( delta_a >= 2*M_PI - amin ) { maxX= data.center.x+vp.magnitude(); }// else // maxX= data.center.x+vp.magnitude()*std::max(cos(amin),cos(amin+delta_a)); // y range vp.set(radius1*sin(angle),-radius2*cos(angle)); amin=RS_Math::correctAngle(getAngle1()+vp.angle()); // to the range of 0 to 2*M_PI amax=RS_Math::correctAngle(getAngle2()+vp.angle()); // to the range of 0 to 2*M_PI if( RS_Math::isAngleBetween(M_PI,amin,amax,isReversed()) ) { //if( (amin<=M_PI &&delta_a >= M_PI - amin) || (amin > M_PI && delta_a >= 3*M_PI - amin)) { minY= data.center.y-vp.magnitude(); }// else // minY=data.center.y +vp.magnitude()*std::min(cos(amin),cos(amin+delta_a)); if( RS_Math::isAngleBetween(2.*M_PI,amin,amax,isReversed()) ) { //if( delta_a >= 2*M_PI - amin ) { maxY= data.center.y+vp.magnitude(); } // else // maxY= data.center.y+vp.magnitude()*std::max(cos(amin),cos(amin+delta_a)); //std::cout<<"New algorithm:\nminX="<<minX<<"\tmaxX="<<maxX<<"\nminY="<<minY<<"\tmaxY="<<maxY<<std::endl; minV.set(minX, minY); maxV.set(maxX, maxY); RS_DEBUG->print("RS_Ellipse::calculateBorders: OK"); }
void RS_Line::draw(RS_Painter* painter, RS_GraphicView* view, double& patternOffset) { if (painter==NULL || view==NULL) { return; } //visible in grahic view if(isVisibleInWindow(view)==false) return; RS_Vector pStart(view->toGui(getStartpoint())); RS_Vector pEnd(view->toGui(getEndpoint())); // std::cout<<"draw line: "<<pStart<<" to "<<pEnd<<std::endl; RS_Vector direction=pEnd-pStart; if(isHelpLayer(true) && direction.squared() > RS_TOLERANCE){ //extend line on a help layer to fill the whole view RS_Vector lb(0,0); RS_Vector rt(view->getWidth(),view->getHeight()); QList<RS_Vector> rect; rect<<lb<<RS_Vector(rt.x,lb.y); rect<<rt<<RS_Vector(lb.x,rt.y); rect<<lb; RS_VectorSolutions sol; RS_Line dLine(pStart,pEnd); for(int i=0;i<4;i++){ RS_Line bLine(rect.at(i),rect.at(i+1)); RS_VectorSolutions sol2=RS_Information::getIntersection(&bLine, &dLine); if( sol2.getNumber()>0 && bLine.isPointOnEntity(sol2.get(0),RS_TOLERANCE)) { sol.push_back(sol2.get(0)); } } switch(sol.getNumber()){ case 2: pStart=sol.get(0); pEnd=sol.get(1); break; case 3: case 4: pStart=sol.get(0); pEnd=sol.get(2); break; default: return; } direction=pEnd-pStart; } double length=direction.magnitude(); patternOffset -= length; if (( !isSelected() && ( getPen().getLineType()==RS2::SolidLine || view->getDrawingMode()==RS2::ModePreview)) ) { //if length is too small, attempt to draw the line, could be a potential bug painter->drawLine(pStart,pEnd); return; } // double styleFactor = getStyleFactor(view); // Pattern: RS_LineTypePattern* pat; if (isSelected()) { // styleFactor=1.; pat = &patternSelected; } else { pat = view->getPattern(getPen().getLineType()); } if (pat==NULL) { // patternOffset -= length; RS_DEBUG->print(RS_Debug::D_WARNING, "RS_Line::draw: Invalid line pattern"); painter->drawLine(pStart,pEnd); return; } // patternOffset = remainder(patternOffset - length-0.5*pat->totalLength,pat->totalLength)+0.5*pat->totalLength; if(length<=RS_TOLERANCE){ painter->drawLine(pStart,pEnd); return; //avoid division by zero } direction/=length; //cos(angle), sin(angle) // Pen to draw pattern is always solid: RS_Pen pen = painter->getPen(); pen.setLineType(RS2::SolidLine); painter->setPen(pen); // index counter int i; // pattern segment length: double patternSegmentLength = pat->totalLength; // create pattern: RS_Vector* dp=new RS_Vector[pat->num > 0?pat->num:0]; double* ds=new double[pat->num > 0?pat->num:0]; if (pat->num >0 ){ for (i=0; i<pat->num; ++i) { // ds[j]=pat->pattern[i] * styleFactor; //fixme, styleFactor support needed ds[i]=pat->pattern[i] ; dp[i] = direction*fabs(ds[i]); } }else { delete[] dp; delete[] ds; RS_DEBUG->print(RS_Debug::D_WARNING,"invalid line pattern for line, draw solid line instread"); painter->drawLine(view->toGui(getStartpoint()), view->toGui(getEndpoint())); return; } double total= remainder(patternOffset-0.5*patternSegmentLength,patternSegmentLength) -0.5*patternSegmentLength; // double total= patternOffset-patternSegmentLength; RS_Vector p1,p2,p3; RS_Vector curP(pStart+direction*total); double t2; for(int j=0;total<length;j=(j+1)%i) { // line segment (otherwise space segment) t2=total+fabs(ds[j]); p3=curP+dp[j]; if (ds[j]>0.0 && t2 > 0.0) { // drop the whole pattern segment line, for ds[i]<0: // trim end points of pattern segment line to line p1 =(total > -0.5)? curP:pStart; p2 =(t2<length+0.5)?p3:pEnd; painter->drawLine(p1,p2); } total=t2; curP=p3; } delete[] dp; delete[] ds; }
RS_Vector RS_Line::getTangentDirection(const RS_Vector& /*point*/)const{ return getEndpoint() - getStartpoint(); }
void RS_Line::draw(RS_Painter* painter, RS_GraphicView* view, double& patternOffset) { if (painter==NULL || view==NULL) { return; } //only draw the visible portion of line QVector<RS_Vector> endPoints(0); RS_Vector vpMin(view->toGraph(0,view->getHeight())); RS_Vector vpMax(view->toGraph(view->getWidth(),0)); QPolygonF visualBox(QRectF(vpMin.x,vpMin.y,vpMax.x-vpMin.x, vpMax.y-vpMin.y)); if( getStartpoint().isInWindowOrdered(vpMin, vpMax) ) endPoints<<getStartpoint(); if( getEndpoint().isInWindowOrdered(vpMin, vpMax) ) endPoints<<getEndpoint(); if(endPoints.size()<2){ QVector<RS_Vector> vertex; for(unsigned short i=0;i<4;i++){ const QPointF& vp(visualBox.at(i)); vertex<<RS_Vector(vp.x(),vp.y()); } for(unsigned short i=0;i<4;i++){ RS_Line line(NULL,RS_LineData(vertex.at(i),vertex.at((i+1)%4))); auto&& vpIts=RS_Information::getIntersection(static_cast<RS_Entity*>(this), &line, true); if( vpIts.size()==0) continue; endPoints<<vpIts.get(0); } } if(endPoints.size()<2) return; if( (endPoints[0] - getStartpoint()).squared() > (endPoints[1] - getStartpoint()).squared() ) std::swap(endPoints[0],endPoints[1]); RS_Vector pStart(view->toGui(endPoints.at(0))); RS_Vector pEnd(view->toGui(endPoints.at(1))); // std::cout<<"draw line: "<<pStart<<" to "<<pEnd<<std::endl; RS_Vector direction=pEnd-pStart; if(isHelpLayer(true) && direction.squared() > RS_TOLERANCE){ //extend line on a help layer to fill the whole view RS_Vector lb(0,0); RS_Vector rt(view->getWidth(),view->getHeight()); QList<RS_Vector> rect; rect<<lb<<RS_Vector(rt.x,lb.y); rect<<rt<<RS_Vector(lb.x,rt.y); rect<<lb; RS_VectorSolutions sol; RS_Line dLine(pStart,pEnd); for(int i=0;i<4;i++){ RS_Line bLine(rect.at(i),rect.at(i+1)); RS_VectorSolutions sol2=RS_Information::getIntersection(&bLine, &dLine); if( sol2.getNumber()>0 && bLine.isPointOnEntity(sol2.get(0),RS_TOLERANCE)) { sol.push_back(sol2.get(0)); } } switch(sol.getNumber()){ case 2: pStart=sol.get(0); pEnd=sol.get(1); break; case 3: case 4: pStart=sol.get(0); pEnd=sol.get(2); break; default: return; } direction=pEnd-pStart; } double length=direction.magnitude(); patternOffset -= length; if (( !isSelected() && ( getPen().getLineType()==RS2::SolidLine || view->getDrawingMode()==RS2::ModePreview)) ) { //if length is too small, attempt to draw the line, could be a potential bug painter->drawLine(pStart,pEnd); return; } // double styleFactor = getStyleFactor(view); // Pattern: RS_LineTypePattern* pat; if (isSelected()) { // styleFactor=1.; pat = &patternSelected; } else { pat = view->getPattern(getPen().getLineType()); } if (pat==NULL) { // patternOffset -= length; RS_DEBUG->print(RS_Debug::D_WARNING, "RS_Line::draw: Invalid line pattern"); painter->drawLine(pStart,pEnd); return; } // patternOffset = remainder(patternOffset - length-0.5*pat->totalLength,pat->totalLength)+0.5*pat->totalLength; if(length<=RS_TOLERANCE){ painter->drawLine(pStart,pEnd); return; //avoid division by zero } direction/=length; //cos(angle), sin(angle) // Pen to draw pattern is always solid: RS_Pen pen = painter->getPen(); pen.setLineType(RS2::SolidLine); painter->setPen(pen); // index counter int i; // pattern segment length: double patternSegmentLength = pat->totalLength; // create pattern: RS_Vector* dp=new RS_Vector[pat->num > 0?pat->num:0]; double* ds=new double[pat->num > 0?pat->num:0]; if (pat->num >0 ){ double dpmm=static_cast<RS_PainterQt*>(painter)->getDpmm(); for (i=0; i<pat->num; ++i) { // ds[j]=pat->pattern[i] * styleFactor; //fixme, styleFactor support needed ds[i]=dpmm*pat->pattern[i]; if( fabs(ds[i]) < 1. ) ds[i] = (ds[i]>=0.)?1.:-1.; dp[i] = direction*fabs(ds[i]); } }else { delete[] dp; delete[] ds; RS_DEBUG->print(RS_Debug::D_WARNING,"invalid line pattern for line, draw solid line instread"); painter->drawLine(view->toGui(getStartpoint()), view->toGui(getEndpoint())); return; } double total= remainder(patternOffset-0.5*patternSegmentLength,patternSegmentLength) -0.5*patternSegmentLength; // double total= patternOffset-patternSegmentLength; RS_Vector p1,p2,p3; RS_Vector curP(pStart+direction*total); double t2; for(int j=0;total<length;j=(j+1)%i) { // line segment (otherwise space segment) t2=total+fabs(ds[j]); p3=curP+dp[j]; if (ds[j]>0.0 && t2 > 0.0) { // drop the whole pattern segment line, for ds[i]<0: // trim end points of pattern segment line to line p1 =(total > -0.5)? curP:pStart; p2 =(t2<length+0.5)?p3:pEnd; painter->drawLine(p1,p2); } total=t2; curP=p3; } delete[] dp; delete[] ds; }