def by3Points(cls, p1, p2, p3): """Create a CADRectangle with 2 points (AD) and a length (X - AD) D-------C | X A-------B """ if not isinstance(p1, (CADPoint, QgsPoint)) or \ not isinstance(p2, (CADPoint, QgsPoint)) or \ not isinstance(p3, (CADPoint, QgsPoint)): raise AttributeError p1 = CADPoint(p1) p2 = CADPoint(p2) p3 = CADPoint(p3) l = CADLine(p1, p2) angle_exist = p1.getAngleOfLineBetweenTwoPoints(p2) side = CADPoint.isCollinear(p1, p2, p3) if side == 0: return None pp = CADLine.pointOnALine(CADLine(p1, p2), p3) length = p3.distance(pp) * side p3 = p2.pointProjected(length, 90 + angle_exist) p4 = p1.pointProjected(length, 90 + angle_exist) return cls(p1, p2, p3, p4)
def by2PointsAndWidth(cls, p1, p2, w): """Create a CADRectangle with 2 points (AD) and the width D-------C | | A-------B """ if not isinstance(p1, (CADPoint, QgsPoint)) or \ not isinstance(p2, (CADPoint, QgsPoint)) or \ not isinstance(w, (float, long, int)): raise AttributeError p1 = CADPoint(p1) p2 = CADPoint(p2) angle_exist = p1.getAngleOfLineBetweenTwoPoints(p2) pt1 = p1 pt2 = p1.pointProjected(w, 90 - angle_exist) pt3 = p2.pointProjected(w, 90 - angle_exist) pt4 = p2 l = CADLine(p1, p2) if CADPoint.isCollinear(pt1, pt2, pt3) == 0: pt2 = p1.pointProjected(w, 90 + angle_exist) pt3 = p2.pointProjected(w, 90 + angle_exist) return cls(pt1, pt2, pt3, pt4)
def byCenter2Points(cls, pc, p1, p2): """Create an ellipse by center and 2 points for axis. /----p2----\ / \ | pc p1 \ / \----------/ """ if not isinstance(pc, (QgsPoint, CADPoint)) \ or not isinstance(p1, (QgsPoint, CADPoint)) \ or not isinstance(p2, (QgsPoint, CADPoint)): raise AttributeError pc = CADPoint(pc) p1 = CADPoint(p1) p2 = CADPoint(p2) angle_exist = pc.getAngleOfLineBetweenTwoPoints(p1) axis_a = pc.distance(p1) pp = CADLine.pointOnALine(CADLine(pc, p1), p2) length = p2.distance(pp) pp2 = pc.pointProjected(length, 90 + angle_exist) axis_b = pc.distance(pp2) return cls(pc, axis_a, axis_b, angle_exist)
def by2Corners(cls, p1, p2, nbEdges=5): """CADRegularPolygon by 2 corners (AB): A----B / \ / \ \ / \------/ """ if not isinstance(p1, (QgsPoint, CADPoint)) or \ not isinstance(p2, (QgsPoint, CADPoint)) or \ not isinstance(nbEdges, (long, int)): raise AttributeError else: p1 = CADPoint(p1) p2 = CADPoint(p2) angle_exist = p1.getAngleOfLineBetweenTwoPoints(p2) pm = p1.midpoint(p2) length = p1.distance(pm) angle = (180.0 - (360.0 / nbEdges)) / 2.0 hypo = length / math.cos(math.radians(angle)) pc = p1.pointProjected(hypo, angle_exist + angle) return cls(pc, p1, nbEdges) return None
def byPointHeightWidthAndAngle(cls, p1, h, w, a): """Create a CADRectangle with base point (A) and the height, width and an angle D-------C | | A-------B """ if not isinstance(p1, (CADPoint, QgsPoint)) \ or not isinstance(h, (float, long, int)) \ or not isinstance(w, (float, long, int)) \ or not isinstance(a, (float, long, int)): raise AttributeError p1 = CADPoint(p1) p2 = p1.pointProjected(w, a) p3 = p2.pointProjected(h, 90 + a) p4 = p1.pointProjected(h, 90 + a) return cls(p1, p2, p3, p4)
def byCenterPointAngle(cls, ptCenter, ptStart, inAngle, direction=1): """Create a CADCirclurArc with by Center C a point A and angle A C | \------/Angle """ if not all([isinstance(p, (CADPoint, QgsPoint)) for p in [ptCenter, ptStart]]) or \ not isinstance(inAngle, (int, long, float)) or \ not isinstance(direction, (int, long)): raise AttributeError ptCenter = CADPoint(ptCenter) ptStart = CADPoint(ptStart) angle = ptCenter.getAngleOfLineBetweenTwoPoints(ptStart) dist = ptCenter.distance(ptStart) if inAngle < 0: inAngle += 360 ptAngle = 0 if direction == 1: ptAngle = angle - (360 - inAngle) elif direction == -1: ptAngle = angle + inAngle ptEnd = ptCenter.pointProjected(dist, ptAngle) ptAngle_arc = ptCenter.getAngleOfLineBetweenTwoPoints( ptStart.midpoint(ptEnd)) if direction == 1: if inAngle < 180: ptAngle_arc = ptAngle_arc + 180 if direction == -1: if inAngle > 180: ptAngle_arc = ptAngle_arc - 180 ptArc = ptCenter.pointProjected(dist, ptAngle_arc) return cls(ptStart, ptEnd, ptArc)
def rotate(self, pr, a): """Rotate a CADRectangle with angle `a` and base point `pr` D-------C | | A-------B """ if not isinstance(pr, (CADPoint, QgsPoint)) \ or not isinstance(a, (float, long, int)): pass else: pr = CADPoint(pr) dist = [pr.distance(i) for i in self.__points] angle_exist = [ pr.getAngleOfLineBetweenTwoPoints(p) for p in self.__points ] for i, p in enumerate(self.__points): self.__points[i] = pr.pointProjected(dist[i], angle_exist[i] + a) self.__update()
class CADCircle(object): def __init__(self, pc, radius): if not isinstance(pc, (CADPoint, QgsPoint)) or \ not isinstance(radius, (float, long, int)): raise AttributeError else: self._center = CADPoint(pc) self._radius = radius def __iter__(self, segments=36): for t in [(2 * math.pi) / segments * i for i in range(segments)]: yield self._center.pointProjected(self._radius, math.degrees(t)) def __repr__(self): return "CADCircle({}, {})".format(self.center, self.radius) def __str__(self): s = "Circle:\n" s += "Center: %s\nRadius: %.3f\n" % (self.center, self.radius) s += "Area: %.3f\nPerimeter: %.3f\n" % (self.area, self.perimeter) return s def quadrant(self): """Return 4 coordinates [East, North, West, South] of circle's quadrant""" N = CADPoint(self._center.x, self._center.y + self._radius) S = CADPoint(self._center.x, self._center.y - self._radius) E = CADPoint(self._center.x + self._radius, self._center.y) W = CADPoint(self._center.x - self._radius, self._center.y) return [E, N, W, S] @property def center(self): return self._center @center.setter def center(self, pc): if not isinstance(pc, (CADPoint, QgsPoint)): raise AttributeError else: self._center = CADPoint(pc) @property def radius(self): return self._radius @radius.setter def radius(self, r): if not isinstance(radius, (float, long, int)): raise AttributeError else: self._radius = r @property def area(self): return math.pi * self.radius * self.radius @property def perimeter(self): return 2 * math.pi * self.radius @classmethod def by2Points(cls, p1, p2): """Create a CADCircle with 2 points (from diameter : AB) /------\ | | A B | | \------/ """ if not all(isinstance(p, (QgsPoint, CADPoint)) for p in [p1, p2]): raise AttributeError p1 = CADPoint(p1) p2 = CADPoint(p2) center = p1.midpoint(p2) radius = p1.distance(center) return cls(center, radius) @classmethod def by3Points(cls, p1, p2, p3, epsilon=1e-8): """Create a CADCircle with 3 points ABC /------\ | | A C | | \--B---/ """ if not all(isinstance(p, (QgsPoint, CADPoint)) for p in [p1, p2, p3]): raise AttributeError p1 = CADPoint(p1) p2 = CADPoint(p2) p3 = CADPoint(p3) # Paul Bourke's algorithm m_Center = CADPoint() m_dRadius = -1 yDelta_a = p2.y - p1.y xDelta_a = p2.x - p1.x yDelta_b = p3.y - p2.y xDelta_b = p3.x - p2.x try: aSlope = yDelta_a / xDelta_a except ZeroDivisionError: return None try: bSlope = yDelta_b / xDelta_b except ZeroDivisionError: return None if (math.fabs(xDelta_a) <= epsilon and math.fabs(yDelta_b) <= epsilon): m_Center.x = (0.5 * (p2.x + p3.x)) m_Center.y = (0.5 * (p1.y + p2.y)) m_dRadius = m_Center.distance(p1) return cls(m_Center, m_dRadius) if math.fabs(aSlope-bSlope) <= epsilon: return None m_Center.x = ( (aSlope * bSlope * (p1.y - p3.y) + bSlope * (p1.x + p2.x) - aSlope * (p2.x + p3.x)) / (2.0 * (bSlope - aSlope)) ) m_Center.y = ( -1.0 * (m_Center.x - (p1.x + p2.x) / 2.0) / aSlope + (p1.y + p2.y) / 2.0 ) m_dRadius = m_Center.distance(p1) return cls(m_Center, m_dRadius) @classmethod def byCenterRadius(cls, pc, radius): """Create a CADCircle with 2 points (from radius : CA) /------\ | | A C | | | \------/ """ if not isinstance(pc, (CADPoint, QgsPoint)) or \ not isinstance(radius, (float, long, int)): raise AttributeError return cls(pc, radius) @classmethod def byCenterDiameter(cls, pc, diameter): """Create a CADCircle with 2 points (from diameter : AB) /------\ | | A B | | \------/ """ if not isinstance(pc, (CADPoint, QgsPoint)) or \ not isinstance(diameter, (float, long, int)): raise AttributeError return cls(pc, radius / 2.0) @classmethod def byCenterPoint(cls, pc, p1): """Create a CADCircle by Extent CA /------\A | | | C | | | \------/ """ if not all(isinstance(p, (QgsPoint, CADPoint)) for p in [p1, pc]): raise AttributeError p1 = CADPoint(p1) pc = CADPoint(pc) return cls(pc, pc.distance(p1)) # Todo: fromTangents def exportToQgsGeometry(self, segments=36): """Export CADCircle to a QgsGeometry (Polygon)""" points = list(self.__iter__(segments)) return QgsGeometry.fromPolygon([[QgsPoint(p.x, p.y) for p in points]]) if CIRCSTRING: def exportToQgsCircularStringV2(self): """Export CADCircle to a QgsCircularStringV2""" quadrant = self.quadrant() quad = [QgsPointV2(p.x, p.y) for p in quadrant] + \ [QgsPointV2(quadrant[0].x, quadrant[0].y)] QgsCircString = QgsCircularStringV2() QgsCircString.setPoints(quad) return QgsCircString
class CADRegularPolygon(object): def __init__(self, pCenter, pCorner, nbEdges=5): if not isinstance(pCenter, (QgsPoint, CADPoint)) \ or not isinstance(pCorner, (QgsPoint, CADPoint)) \ or not isinstance(nbEdges, (long, int)): raise AttributeError else: self._center = CADPoint(pCenter) self._corner = CADPoint(pCorner) self._nbEdges = nbEdges self.__points = self.__updatePoints() # self._angle def __str__(self): s = "Regular polygon:\n" + \ "Edges: {}".format(self._nbEdges) + '\n' + \ "Center: {}".format(self._center) + '\n' + \ "Corner: {}".format(self._corner) + '\n' return s def __repr__(self, precision=3): s = "CADRegularPolygon(" s += "QgsPoint(%s)" % self._center s += ", QgsPoint(%s)" % self._corner s += ", %s)" % self._nbEdges return s def __eq__(self, other): if not isinstance(other, (QgsPoint, CADPoint)): raise AttributeError if CADPoint.pointsCompare(self._center, other._center) \ and self._nbEdges == other._nbEdges: return True return False def __iter__(self): i = 0 while i < self._nbEdges: yield self.__points[i] i += 1 def __update(self): self.__updatePoints() self.__updateRadius() self.__updateAngle() def __updateAngle(self): self._angle = self._center.getAngleOfLineBetweenTwoPoints(self._corner) def __updateRadius(self): self._radius = self._center.distance(self._corner) def __updatePoints(self): n = 1 center = self._center corner = self._corner r = center.distance(corner) points = [] angle_add = 2 * math.pi / self._nbEdges angle_start = math.atan2(corner.y - center.y, corner.x - center.x) angle = angle_start while (n <= self._nbEdges): angle += angle_add if angle_add > 0.0 and angle > math.pi: angle -= 2 * math.pi c2 = center.pointProjected(r, math.degrees(angle)) points.append(c2) n += 1 return points @property def nbEdges(self): """Return the number of sides/edges of the RegularPolygon """ return self._nbEdges @nbEdges.setter def nbEdges(self, n): """Set the number of sides/edges of the RegularPolygon """ if not isinstance(n, (int, long)): raise AttributeError self._nbEdges = n self.__update() @property def center(self): """The center of the RegularPolygon This is also the center of the circumscribing circle. """ return self._center @center.setter def center(self, pc): """Set the center of the RegularPolygon """ if not isinstance(pc, (QgsPoint, CADPoint)): raise AttributeError angle_exist = self._rotation dist_exist = self._radius self._center = CADPoint(pc) self._corner = self._center.pointProjected(dist_exist, angle_exist) self._update() @property def circumcenter(self): """ Alias for center. """ return self.center @property def corner(self): """Return the first corner (edge) of the RegularPolygon """ return self._corner @corner.setter def corner(self, pc): """Set the first corner (edge) of the RegularPolygon """ if not isinstance(pc, (QgsPoint, CADPoint)): raise AttributeError self._corner = CADPoint(pc) self.__update() @property def length(self): """Return the length of the sides. """ return self._radius * 2 * math.sin(math.pi / self._nbEdges) @property def radius(self): """Radius of the RegularPolygon This is also the radius of the circumscribing circle. """ return self._radius @property def circumradius(self): """ Alias for radius. """ return self.radius @property def angle(self): """Angle by which the RegularPolygon is rotated """ return self._angle @property def apothem(self): """The inradius of the RegularPolygon. The apothem/inradius is the radius of the inscribed circle. """ return self.radius * math.cos(math.pi / self._nbEdges) @property def inradius(self): """ Alias for apothem. """ return self.apothem @property def interior_angle(self): """Measure of the interior angles. """ return (self._nbEdges - 2) * math.pi / self._nbEdges @property def exterior_angle(self): """Measure of the exterior angles. """ return 2 * math.pi / self._nbEdges @property def circumcircle(self): """The circumcircle of the RegularPolygon. """ return CADCircle(self.center, self.radius) @property def incircle(self): """The incircle of the RegularPolygon. """ return CADCircle(self.center, self.apothem) @property def area(self): """Returns the area. """ r, n, l = self._radius, self._nbEdges, self.length return r * n * l**2 / (4 * math.tan(math.pi / n)) @classmethod def by2Corners(cls, p1, p2, nbEdges=5): """CADRegularPolygon by 2 corners (AB): A----B / \ / \ \ / \------/ """ if not isinstance(p1, (QgsPoint, CADPoint)) or \ not isinstance(p2, (QgsPoint, CADPoint)) or \ not isinstance(nbEdges, (long, int)): raise AttributeError else: p1 = CADPoint(p1) p2 = CADPoint(p2) angle_exist = p1.getAngleOfLineBetweenTwoPoints(p2) pm = p1.midpoint(p2) length = p1.distance(pm) angle = (180.0 - (360.0 / nbEdges)) / 2.0 hypo = length / math.cos(math.radians(angle)) pc = p1.pointProjected(hypo, angle_exist + angle) return cls(pc, p1, nbEdges) return None @classmethod def byCenterAndCorner(cls, p1, p2, nbEdges=5): """CADRegularPolygon by center C and a corner A: A----- / \ / C \ \ / \------/ """ if not isinstance(p1, (QgsPoint, CADPoint)) or \ not isinstance(p2, (QgsPoint, CADPoint)) or \ not isinstance(nbEdges, (long, int)): raise AttributeError else: return cls(p1, p2, nbEdges) return None def exportToQgsGeometry(self): """Export CADRegularPolygon to a QgsGeometry (Polygon)""" return QgsGeometry.fromPolygon( [[QgsPoint(p.x, p.y) for p in self.__points]])