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 fromFoci(cls, f1, f2, f3): """Create an ellipse from foci. f1 and f2 are foci f3 is the axis_b /----f3----\ / \ | f1 f2 | \ / \----------/ """ if not isinstance(f1, (QgsPoint, CADPoint)) \ or not isinstance(f2, (QgsPoint, CADPoint)) \ or not isinstance(f3, (QgsPoint, CADPoint)): raise AttributeError f1 = CADPoint(f1) f2 = CADPoint(f2) f3 = CADPoint(f3) dist_f1f2 = f1.distance(f2) dist_f1f3 = f1.distance(f3) dist_f2f3 = f2.distance(f3) dist_tot = dist_f1f3 + dist_f2f3 angle_exist = f1.getAngleOfLineBetweenTwoPoints(f2) center_f1f2 = f1.midpoint(f2) axis_a = dist_tot / 2.0 axis_b = math.sqrt((dist_tot / 2.0)**2.0 - (dist_f1f2 / 2.0)**2.0) return cls(center_f1f2, axis_a, axis_b, angle_exist)
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)
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 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 nearestPoints(self, other): if isinstance(other, (QgsPoint, CADPoint)): other = CADPoint(other) dist = [other.distance(p) for p in self.points] neareastDist = min(dist) idx = dist.index(neareastDist) return (self.points[idx], idx) else: raise AttributeError
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))
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)
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()
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 squareFromCenter(cls, p1, p2): """Create a CADRectangle (square) with 2 points (from extent : AC) D---C | | A---B """ if isinstance(p1, (CADPoint, QgsPoint)) and \ isinstance(p2, (CADPoint, QgsPoint)): p1 = CADPoint(p1) p2 = CADPoint(p2) distance = p2.distance(p1) offset = distance / math.sqrt(2) pt1 = (-offset, -offset) pt2 = (offset, -offset) pt3 = (offset, offset) pt4 = (-offset, offset) points = [pt1, pt2, pt3, pt4] polygon = [CADPoint(p1.x + i[0], p1.y + i[1]) for i in points] return cls(polygon[0], polygon[1], polygon[2], polygon[3]) else: raise AttributeError("Arguments must be type CADPoint, send : " + str(type(p1)) + " and " + str(type(p2))) return cls()
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]])