TEST(MatrixTest, rotate) { RotationMatrix m(90); DPair p = m*DPair(2,0); EXPECT_NEAR(p.x(), 0, 0.0000000001); EXPECT_NEAR(p.y(), 2, 0.0000000001); p = m*p; EXPECT_NEAR(p.x(), -2, 0.0000000001); EXPECT_NEAR(p.y(), 0, 0.0000000001); }
TEST(MatrixTest, scale) { ScalingMatrix m(2,2); DPair p = m*DPair(3,3); EXPECT_DOUBLE_EQ(p.x(), 6); EXPECT_DOUBLE_EQ(p.y(), 6); m = ScalingMatrix(-2,-2); p = m*DPair(3,3); EXPECT_DOUBLE_EQ(p.x(), -6); EXPECT_DOUBLE_EQ(p.y(), -6); }
DPair Matrix::operator * (const DPair &p) const { double pp[] = {p.x(), p.y(), 1}; double ret[]= {0, 0}; for (int i=0; i < 2; i++) for (int j=0; j < 3; j++) ret[i] += _values[i][j] * pp[j]; return DPair(ret[0], ret[1]); }
TEST(PairTest, constructor) { DPair p(-1.5, 2); ASSERT_EQ(p.x(), -1.5); ASSERT_EQ(p.y(), 2); DPair q = p; ASSERT_EQ(q.x(), -1.5); ASSERT_EQ(q.y(), 2); }
void BoundingBox::transform (const Matrix &tm) { if (!_locked) { DPair ul = tm * DPair(_lrx, _lry); DPair lr = tm * DPair(_ulx, _uly); DPair ll = tm * DPair(_ulx, _lry); DPair ur = tm * DPair(_lrx, _uly); _ulx = min(min(ul.x(), lr.x()), min(ur.x(), ll.x())); _uly = min(min(ul.y(), lr.y()), min(ur.y(), ll.y())); _lrx = max(max(ul.x(), lr.x()), max(ur.x(), ll.x())); _lry = max(max(ul.y(), lr.y()), max(ur.y(), ll.y())); } }
/** Creates the SVG element that will a the line. * @param[in] p1 first endpoint in PS point units * @param[in] p2 second endpoint in PS point units * @param[in] c1 cut method of first endpoint ('h', 'v' or 'p') * @param[in] c2 cut method of second endpoint ('h', 'v' or 'p') * @param[in] lw line width in PS point units * @param[in] actions object providing the actions that can be performed by the SpecialHandler */ static void create_line (const DPair &p1, const DPair &p2, char c1, char c2, double lw, SpecialActions &actions) { unique_ptr<XMLElementNode> node; DPair dir = p2-p1; if (dir.x() == 0 || dir.y() == 0 || (c1 == 'p' && c2 == 'p')) { // draw regular line node = util::make_unique<XMLElementNode>("line"); node->addAttribute("x1", p1.x()); node->addAttribute("y1", p1.y()); node->addAttribute("x2", p2.x()); node->addAttribute("y2", p2.y()); node->addAttribute("stroke-width", lw); node->addAttribute("stroke", actions.getColor().svgColorString()); // update bounding box DPair cv = cut_vector('p', dir, lw); actions.embed(p1+cv); actions.embed(p1-cv); actions.embed(p2+cv); actions.embed(p2-cv); } else { // draw polygon DPair cv1 = cut_vector(c1, dir, lw); DPair cv2 = cut_vector(c2, dir, lw); DPair q11 = p1+cv1, q12 = p1-cv1; DPair q21 = p2+cv2, q22 = p2-cv2; ostringstream oss; oss << XMLString(q11.x()) << ',' << XMLString(q11.y()) << ' ' << XMLString(q12.x()) << ',' << XMLString(q12.y()) << ' ' << XMLString(q22.x()) << ',' << XMLString(q22.y()) << ' ' << XMLString(q21.x()) << ',' << XMLString(q21.y()); node = util::make_unique<XMLElementNode>("polygon"); node->addAttribute("points", oss.str()); if (actions.getColor() != Color::BLACK) node->addAttribute("fill", actions.getColor().svgColorString()); // update bounding box actions.embed(q11); actions.embed(q12); actions.embed(q21); actions.embed(q22); } actions.appendToPage(std::move(node)); }
/** Computes the "cut vector" that is used to compute the line shape. * Because each line has a width > 0 the actual shape of the line is a tetragon. * The 4 vertices can be influenced by the cut parameter c that specifies * a horizontal, vertical or orthogonal cut of a line end. Depending on c and the * line's slope a cut vector v can be computed that, relatively to endpoint p, denotes * the 2 vertices of that line end: v1=p+v and v2=p-v. * @param[in] cuttype character identifying the cut direction ('h', 'v' or 'p') * @param[in] linedir direction vector of line to be drawn * @param[in] lw width of line to be drawn * @return the "cut vector" */ static DPair cut_vector (char cuttype, const DPair &linedir, double linewidth) { DPair cut; switch (cuttype) { case 'v': // vertical if (linedir.x() != 0) { double slope = linedir.y()/linedir.x(); double h = sqrt(linewidth*linewidth*(1+slope*slope)); cut.y(h/2); } break; case 'h': // horizontal if (linedir.y() != 0) { double slope = linedir.x()/linedir.y(); double h = sqrt(linewidth*linewidth*(1+slope*slope)); double sgn = slope < 0 ? 1.0 : -1.0; cut.x(h*sgn/2); } break; default: // c == 'p': perpendicular to the line vector if (linedir.x() != 0 && linedir.y() != 0) return linedir.ortho()/linedir.length() * (linewidth/2); } return cut; }
BoundingBox::BoundingBox (const DPair &p1, const DPair &p2) : _ulx(min(p1.x(), p2.x())), _uly(min(p1.y(), p2.y())), _lrx(max(p1.x(), p2.x())), _lry(max(p1.y(), p2.y())), _valid(true), _locked(false) { }
/** Embeds a virtual circle into the box and enlarges it accordingly. * @param[in] c center of the circle * @param[in] r radius of the circle */ void BoundingBox::embed (const DPair &c, double r) { embed(BoundingBox(c.x()-r, c.y()-r, c.x()+r, c.y()+r)); }