def addRevArcSeg(self, x1, y1, x2, y2, cx, cy, arcDir): """Add a 360 degree revolved arc to this Patch. x1, y1 -- start vertex x2, y2 -- end vertex cx, cy -- center point arcDir -- 'cclw' or 'clw' It is assumes the points are in the XZ plane and the axis of revolution is parallel to the vector (0, 0, 1), and passes through the point (0, 0, 0). """ a = x1 - cx b = y1 - cy r = sqrt(a*a + b*b) arc = Arc.fromVectors(QVector2D(a, b), QVector2D(x2 - cx, y2 - cy), r, arcDir == 'cclw') # TODO: By halving the mesh segs ( * 0.5), fewer triangles are # created. Shading is ok but arc edges look blocky. # angstep = 360.0 / (self._mesh.segs * 0.5) angstep = 360.0 / self._mesh.segs # minimum 2 segments in the arc segs = max(int(abs(arc.span()) / angstep), 3) step = arc.span() / segs sa = arc.startAngle() a1 = radians(sa) sa1 = sin(a1) ca1 = cos(a1) for i in range(1, segs): a2 = radians(sa + step * i) sa2 = sin(a2) ca2 = cos(a2) x1 = cx + r * ca1 y1 = cy + r * sa1 x2 = cx + r * ca2 y2 = cy + r * sa2 self.addRevLineSeg(x1, y1, x2, y2) a1 = a2 sa1 = sa2 ca1 = ca2 if i == 1: # only blend the first strip self._mesh.blendTangent(False) # last strip else: a2 = radians(arc.endAngle()) x1 = cx + r * ca1 y1 = cy + r * sa1 x2 = cx + r * cos(a2) y2 = cy + r * sin(a2) self.addRevLineSeg(x1, y1, x2, y2)
def config(self, specMap={}): if not super(AngleDim, self).config(specMap): return pp = QPainterPath() labelP = self.specMap['pos'] tb = self.dimText.sceneBoundingRect() l1 = self.specMap['line1'] l2 = self.specMap['line2'] outside = self.specMap['outside'] if l1.isNull() or l2.isNull(): raise AngleDimException("refrenced line has zero length") # line intersection point xsectP = QPointF() xsectType = l1.intersect(l2, xsectP) if xsectType == QLineF.NoIntersection: raise AngleDimException("reference lines are parallel") # radius of arc leaders that pass through the label's center point labelV = QVector2D(labelP - xsectP) dp = labelV.dotProduct radius = labelV.length() # find fixed leader span angle chordLen = self.scene().pixelsToScene(self.leaderLen) rsq = radius * radius res = (rsq + rsq - chordLen * chordLen) / (2 * rsq) fixedLeaderSpan = degrees(acos(clamp(res, 0.0, 1.0))) # guess the line vectors v1 = QVector2D(l1.p2() - l1.p1()).normalized() v2 = QVector2D(l2.p2() - l2.p1()).normalized() # maybe reverse the line vectors so they point towards the quadrant # specified quadV = self.specMap['quadV'].normalized() if dp(v1, quadV) <= 0: v1 = -v1 if dp(v2, quadV) <= 0: v2 = -v2 # angle bisector bisectV = (v1 + v2).normalized() # angle bisector rotated 90 degrees cclw bisectV90 = QVector2D(-bisectV.y(), bisectV.x()) # determine which side of the bisector the arrow tips lay lL = l1 rL = l2 lV = v1 rV = v2 lAp = xsectP + (lV * radius).toPointF() rAp = xsectP + (rV * radius).toPointF() if dp(bisectV90, v1) <= 0.0: lL, rL, lV, rV, lAp, rAp = rL, lL, rV, lV, rAp, lAp # find where the label lays lableV = labelV.normalized() lVperp = QVector2D(-lV.y(), lV.x()) rVperp = QVector2D(rV.y(), -rV.x()) # leader arc rectangle rect = QRectF(xsectP.x() - radius, xsectP.y() + radius, radius * 2, -radius * 2) # When left and right are mentioned, they are relative to the # intersection point of the two reference lines, looking in the # direction of the angle bisector. # label left of quad if dp(labelV, lVperp) > 0.0: if outside: # leader from lable to left arrow arc = Arc.fromVectors(labelV, lV, radius, False) arc.center(xsectP) clipP = xsectArcRect1(arc, tb) if clipP: arc = Arc.fromVectors(QVector2D(clipP - xsectP), lV, radius, False) arc.center(xsectP) pp.arcMoveTo(rect, arc.start()) pp.arcTo(rect, arc.start(), arc.span()) # fixed leader from right arrow sa = vectorToAbsAngle(rV) pp.arcMoveTo(rect, sa) pp.arcTo(rect, sa, -fixedLeaderSpan) else: # leader from label, through left arrow, to right arrow arc = Arc.fromVectors(labelV, rV, radius, False) arc.center(xsectP) clipP = xsectArcRect1(arc, tb) if clipP: arc = Arc.fromVectors(QVector2D(clipP - xsectP), rV, radius, False) arc.center(xsectP) pp.arcMoveTo(rect, arc.start()) pp.arcTo(rect, arc.start(), arc.span()) # label right of quad elif dp(labelV, rVperp) > 0.0: if outside: # leader from label to right arrow arc = Arc.fromVectors(labelV, rV, radius) arc.center(xsectP) clipP = xsectArcRect1(arc, tb) if clipP: arc = Arc.fromVectors(QVector2D(clipP - xsectP), rV, radius) arc.center(xsectP) pp.arcMoveTo(rect, arc.start()) pp.arcTo(rect, arc.start(), arc.span()) # fixed length leader from left arrow sa = vectorToAbsAngle(lV) pp.arcMoveTo(rect, sa) pp.arcTo(rect, sa, fixedLeaderSpan) else: # leader from label, through right arrow, to left arrow arc = Arc.fromVectors(labelV, lV, radius) arc.center(xsectP) clipP = xsectArcRect1(arc, tb) if clipP: arc = Arc.fromVectors(QVector2D(clipP - xsectP), lV, radius) arc.center(xsectP) pp.arcMoveTo(rect, arc.start()) pp.arcTo(rect, arc.start(), arc.span()) # label inside quad else: if outside: # fixed length leader from right arrow sa = vectorToAbsAngle(rV) pp.arcMoveTo(rect, sa) pp.arcTo(rect, sa, -fixedLeaderSpan) # from left arrow sa = vectorToAbsAngle(lV) pp.arcMoveTo(rect, sa) pp.arcTo(rect, sa, fixedLeaderSpan) else: # leader from label to left arrow clipP = xsectArcRect1(Arc.fromVectors(labelV, lV, radius), tb) if clipP: arc = Arc.fromVectors(QVector2D(clipP - xsectP), lV, radius) pp.arcMoveTo(rect, arc.start()) pp.arcTo(rect, arc.start(), arc.span()) # to right arrow clipP = xsectArcRect1(Arc.fromVectors(labelV, rV, radius, False), tb) if clipP: arc = Arc.fromVectors(QVector2D(clipP - xsectP), rV, radius, False) pp.arcMoveTo(rect, arc.start()) pp.arcTo(rect, arc.start(), arc.span()) # arrow tips if outside: self.arrow1.config({'pos': lAp, 'dir': -QVector2D(-lV.y(), lV.x())}) self.arrow2.config({'pos': rAp, 'dir': -QVector2D(rV.y(), -rV.x())}) else: self.arrow1.config({'pos': lAp, 'dir': QVector2D(-lV.y(), lV.x())}) self.arrow2.config({'pos': rAp, 'dir': QVector2D(rV.y(), -rV.x())}) # Find the end points closest to their arrow tips for extension lines # Don't render the extension if the arrow tip is on its line. p1 = None if not isPointOnLineSeg(lAp, lL): p1 = lL.p1() if QVector2D(lAp - lL.p2()).length() \ < QVector2D(lAp - p1).length(): p1 = lL.p2() p2 = None if not isPointOnLineSeg(rAp, rL): p2 = rL.p1() if QVector2D(rAp - rL.p2()).length() \ < QVector2D(rAp - p2).length(): p2 = rL.p2() self._addExtensionLines(p1, p2, lAp, rAp, pp) self.setPath(pp)
def config(self, specMap={}): if not super(RadiusDim, self).config(specMap): return arcRadius = self.specMap['arc'].radius() pos = self.specMap['pos'] pp = QPainterPath() arcCenter = self.specMap['arc'].center() # is the label outside the arc labelOutside = QVector2D(pos - arcCenter).length() > arcRadius # is the label to the right of the arc's y axis? labelRight = pos.x() > arcCenter.x() br = self.dimText.sceneBoundingRect().normalized() # arrow pointing toward arc center point if self.specMap['outside']: if labelOutside: jogLen = self.scene().pixelsToScene(self.jogLineLen) if labelRight: jogV = QVector2D(-1, 0).normalized() jogSp = QPointF(br.left(), br.center().y()) else: jogV = QVector2D(1, 0).normalized() jogSp = QPointF(br.right(), br.center().y()) jogEp = jogSp + (jogV * jogLen).toPointF() ap = pointOnArc(jogEp, arcCenter, arcRadius) av = QVector2D(arcCenter - jogEp) self.arrow.config({'pos': ap, 'dir': av}) pp.moveTo(jogSp) pp.lineTo(jogEp) pp.lineTo(ap) # label inside arc else: leaderLen = self.scene().pixelsToScene(self.leaderLen) ap = pointOnArc(pos, arcCenter, arcRadius) av = QVector2D(arcCenter - ap).normalized() self.arrow.config({'pos': ap, 'dir': av}) # fixed len arrow leader ep = ap + (av * -1 * leaderLen).toPointF() pp.moveTo(ap) pp.lineTo(ep) # arrow pointing away from arc center else: if labelOutside: jogLen = self.scene().pixelsToScene(self.jogLineLen) if labelRight: jogV = QVector2D(-1, 0).normalized() jogSp = QPointF(br.left(), br.center().y()) else: jogV = QVector2D(1, 0).normalized() jogSp = QPointF(br.right(), br.center().y()) jogEp = jogSp + (jogV * jogLen).toPointF() ap = pointOnArc(jogEp, arcCenter, arcRadius) av = QVector2D(ap - arcCenter) self.arrow.config({'pos': ap, 'dir': av}) pp.moveTo(arcCenter) pp.lineTo(ap) pp.lineTo(jogEp) pp.lineTo(jogSp) # label inside the arc else: ap = pointOnArc(pos, arcCenter, arcRadius) av = QVector2D(ap - arcCenter) self.arrow.config({'pos': ap, 'dir': av}) # label to arc center leader xp1 = xsectLineRect1(QLineF(pos, arcCenter), br) if xp1: pp.moveTo(xp1) pp.lineTo(arcCenter) # label to arrow leader xp2 = xsectLineRect1(QLineF(pos, ap), br) if xp2: pp.moveTo(xp2) pp.lineTo(ap) # extension arc arc = self.specMap['arc'] arcStart = arc.start() arcSpan = arc.span() if not isPointOnArc(ap, arcCenter, arcStart, arcSpan): # arc rect p = QPointF(arcRadius, arcRadius) r = QRectF(arcCenter - p, arcCenter + p) bisV = arc.bisector() # arc center to arrow tip vector apV = QVector2D(ap - arcCenter).normalized() # bisector rotated 90 bis90V = QVector2D(-bisV.y(), bisV.x()) spV = arc.startAngleVector() spLeftOfBisector = spV.dotProduct(spV, bis90V) >= 0.0 epV = arc.endAngleVector() # is the arrow tip to the left of the bisector? if bis90V.dotProduct(apV, bis90V) >= 0.0: # is the start point left of the bisector? if spLeftOfBisector: extensionArc = Arc.fromVectors(spV, apV, arcRadius, True) else: extensionArc = Arc.fromVectors(epV, apV, arcRadius, True) pp.arcMoveTo(r, -extensionArc.start() - self.gapAngle) pp.arcTo(r, -extensionArc.start() - self.gapAngle, -extensionArc.span() - self.extAngle) # arrow tip to right of bisector else: # is the start point left of the bisector? if spLeftOfBisector: extensionArc = Arc.fromVectors(epV, apV, arcRadius, False) else: extensionArc = Arc.fromVectors(spV, apV, arcRadius, False) pp.arcMoveTo(r, -extensionArc.start() + self.gapAngle) pp.arcTo(r, -extensionArc.start() + self.gapAngle, -extensionArc.span() + self.extAngle) self.setPath(pp)
def config(self, specMap={}): if not super(AngleDim, self).config(specMap): return pp = QPainterPath() labelP = self.specMap['pos'] tb = self.dimText.sceneBoundingRect() l1 = self.specMap['line1'] l2 = self.specMap['line2'] outside = self.specMap['outside'] if l1.isNull() or l2.isNull(): raise AngleDimException("refrenced line has zero length") # line intersection point xsectP = QPointF() xsectType = l1.intersect(l2, xsectP) if xsectType == QLineF.NoIntersection: raise AngleDimException("reference lines are parallel") # radius of arc leaders that pass through the label's center point labelV = QVector2D(labelP - xsectP) dp = labelV.dotProduct radius = labelV.length() # find fixed leader span angle chordLen = self.scene().pixelsToScene(self.leaderLen) rsq = radius * radius res = (rsq + rsq - chordLen * chordLen) / (2 * rsq) fixedLeaderSpan = degrees(acos(clamp(res, 0.0, 1.0))) # guess the line vectors v1 = QVector2D(l1.p2() - l1.p1()).normalized() v2 = QVector2D(l2.p2() - l2.p1()).normalized() # maybe reverse the line vectors so they point towards the quadrant # specified quadV = self.specMap['quadV'].normalized() if dp(v1, quadV) <= 0: v1 = -v1 if dp(v2, quadV) <= 0: v2 = -v2 # angle bisector bisectV = (v1 + v2).normalized() # angle bisector rotated 90 degrees cclw bisectV90 = QVector2D(-bisectV.y(), bisectV.x()) # determine which side of the bisector the arrow tips lay lL = l1 rL = l2 lV = v1 rV = v2 lAp = xsectP + (lV * radius).toPointF() rAp = xsectP + (rV * radius).toPointF() if dp(bisectV90, v1) <= 0.0: lL, rL, lV, rV, lAp, rAp = rL, lL, rV, lV, rAp, lAp # find where the label lays lableV = labelV.normalized() lVperp = QVector2D(-lV.y(), lV.x()) rVperp = QVector2D(rV.y(), -rV.x()) # leader arc rectangle rect = QRectF(xsectP.x() - radius, xsectP.y() + radius, radius * 2, -radius * 2) # When left and right are mentioned, they are relative to the # intersection point of the two reference lines, looking in the # direction of the angle bisector. # label left of quad if dp(labelV, lVperp) > 0.0: if outside: # leader from lable to left arrow arc = Arc.fromVectors(labelV, lV, radius, False) arc.center(xsectP) clipP = xsectArcRect1(arc, tb) if clipP: arc = Arc.fromVectors(QVector2D(clipP - xsectP), lV, radius, False) arc.center(xsectP) pp.arcMoveTo(rect, arc.start()) pp.arcTo(rect, arc.start(), arc.span()) # fixed leader from right arrow sa = vectorToAbsAngle(rV) pp.arcMoveTo(rect, sa) pp.arcTo(rect, sa, -fixedLeaderSpan) else: # leader from label, through left arrow, to right arrow arc = Arc.fromVectors(labelV, rV, radius, False) arc.center(xsectP) clipP = xsectArcRect1(arc, tb) if clipP: arc = Arc.fromVectors(QVector2D(clipP - xsectP), rV, radius, False) arc.center(xsectP) pp.arcMoveTo(rect, arc.start()) pp.arcTo(rect, arc.start(), arc.span()) # label right of quad elif dp(labelV, rVperp) > 0.0: if outside: # leader from label to right arrow arc = Arc.fromVectors(labelV, rV, radius) arc.center(xsectP) clipP = xsectArcRect1(arc, tb) if clipP: arc = Arc.fromVectors(QVector2D(clipP - xsectP), rV, radius) arc.center(xsectP) pp.arcMoveTo(rect, arc.start()) pp.arcTo(rect, arc.start(), arc.span()) # fixed length leader from left arrow sa = vectorToAbsAngle(lV) pp.arcMoveTo(rect, sa) pp.arcTo(rect, sa, fixedLeaderSpan) else: # leader from label, through right arrow, to left arrow arc = Arc.fromVectors(labelV, lV, radius) arc.center(xsectP) clipP = xsectArcRect1(arc, tb) if clipP: arc = Arc.fromVectors(QVector2D(clipP - xsectP), lV, radius) arc.center(xsectP) pp.arcMoveTo(rect, arc.start()) pp.arcTo(rect, arc.start(), arc.span()) # label inside quad else: if outside: # fixed length leader from right arrow sa = vectorToAbsAngle(rV) pp.arcMoveTo(rect, sa) pp.arcTo(rect, sa, -fixedLeaderSpan) # from left arrow sa = vectorToAbsAngle(lV) pp.arcMoveTo(rect, sa) pp.arcTo(rect, sa, fixedLeaderSpan) else: # leader from label to left arrow clipP = xsectArcRect1(Arc.fromVectors(labelV, lV, radius), tb) if clipP: arc = Arc.fromVectors(QVector2D(clipP - xsectP), lV, radius) pp.arcMoveTo(rect, arc.start()) pp.arcTo(rect, arc.start(), arc.span()) # to right arrow clipP = xsectArcRect1( Arc.fromVectors(labelV, rV, radius, False), tb) if clipP: arc = Arc.fromVectors(QVector2D(clipP - xsectP), rV, radius, False) pp.arcMoveTo(rect, arc.start()) pp.arcTo(rect, arc.start(), arc.span()) # arrow tips if outside: self.arrow1.config({ 'pos': lAp, 'dir': -QVector2D(-lV.y(), lV.x()) }) self.arrow2.config({ 'pos': rAp, 'dir': -QVector2D(rV.y(), -rV.x()) }) else: self.arrow1.config({'pos': lAp, 'dir': QVector2D(-lV.y(), lV.x())}) self.arrow2.config({'pos': rAp, 'dir': QVector2D(rV.y(), -rV.x())}) # Find the end points closest to their arrow tips for extension lines # Don't render the extension if the arrow tip is on its line. p1 = None if not isPointOnLineSeg(lAp, lL): p1 = lL.p1() if QVector2D(lAp - lL.p2()).length() \ < QVector2D(lAp - p1).length(): p1 = lL.p2() p2 = None if not isPointOnLineSeg(rAp, rL): p2 = rL.p1() if QVector2D(rAp - rL.p2()).length() \ < QVector2D(rAp - p2).length(): p2 = rL.p2() self._addExtensionLines(p1, p2, lAp, rAp, pp) self.setPath(pp)