From b7907e0fd89e0effae1eb66994ce1476cfd15895 Mon Sep 17 00:00:00 2001 From: paullee0 Date: Fri, 12 Jul 2019 03:08:00 +0800 Subject: [PATCH] ArchWall_DraftGeomUtils Multi-Width support added Discussion:- https://forum.freecadweb.org/viewtopic.php?f=23&t=36772&p=319829#p319829 --- src/Mod/Arch/ArchWall.py | 39 +++++++++++++++++---- src/Mod/Draft/DraftGeomUtils.py | 60 ++++++++++++++++++++++++++++----- 2 files changed, 83 insertions(+), 16 deletions(-) diff --git a/src/Mod/Arch/ArchWall.py b/src/Mod/Arch/ArchWall.py index b188288cfc..1b0c52a146 100644 --- a/src/Mod/Arch/ArchWall.py +++ b/src/Mod/Arch/ArchWall.py @@ -534,6 +534,11 @@ class _Wall(ArchComponent.Component): obj.addProperty("App::PropertyLength","Length","Wall",QT_TRANSLATE_NOOP("App::Property","The length of this wall. Not used if this wall is based on an underlying object")) if not "Width" in lp: obj.addProperty("App::PropertyLength","Width","Wall",QT_TRANSLATE_NOOP("App::Property","The width of this wall. Not used if this wall is based on a face")) + + # To be combined into Width when PropertyLengthList is available + if not "WidthsOfWall" in lp: + obj.addProperty("App::PropertyFloatList","WidthsOfWall","Wall",QT_TRANSLATE_NOOP("App::Property","The widths of each segment of wall (The 1st value override 'Width' attribute for 1st segment of wall; if a value is zero, 1st value of 'WidthsOfWall' attribute will be followed)")) # see DraftGeomUtils.offsetwire() + if not "Height" in lp: obj.addProperty("App::PropertyLength","Height","Wall",QT_TRANSLATE_NOOP("App::Property","The height of this wall. Keep 0 for automatic. Not used if this wall is based on a solid")) if not "Align" in lp: @@ -790,7 +795,19 @@ class _Wall(ArchComponent.Component): # multifuses not considered here return data length = obj.Length.Value - width = obj.Width.Value + + # TODO currently layers were not supported when len(basewires) > 0 + width = 0 + if obj.WidthsOfWall: + if obj.WidthsOfWall[0]: + width = obj.WidthsOfWall[0] + if not width: + if obj.Width: + width = obj.Width.Value + else: + print("Width or Widths Of Wall [0] should not be 0") + return + height = obj.Height.Value if not height: for p in obj.InList: @@ -861,7 +878,8 @@ class _Wall(ArchComponent.Component): for c in Part.sortEdges(cluster): self.basewires.append(Part.Wire(c)) - if self.basewires and width: + if self.basewires: # and width: # width already tested earlier... + if (len(self.basewires) == 1) and layers: self.basewires = [self.basewires[0] for l in layers] layeroffset = 0 @@ -886,7 +904,9 @@ class _Wall(ArchComponent.Component): if off: dvec2 = DraftVecUtils.scaleTo(dvec,off) wire = DraftGeomUtils.offsetWire(wire,dvec2) - w2 = DraftGeomUtils.offsetWire(wire,dvec) + + w2 = DraftGeomUtils.offsetWire(wire,dvec,False, False, obj.WidthsOfWall) + w1 = Part.Wire(Part.__sortEdges__(wire.Edges)) sh = DraftGeomUtils.bind(w1,w2) elif obj.Align == "Right": @@ -901,7 +921,9 @@ class _Wall(ArchComponent.Component): if off: dvec2 = DraftVecUtils.scaleTo(dvec,off) wire = DraftGeomUtils.offsetWire(wire,dvec2) - w2 = DraftGeomUtils.offsetWire(wire,dvec) + + w2 = DraftGeomUtils.offsetWire(wire,dvec,False, False, obj.WidthsOfWall) + w1 = Part.Wire(Part.__sortEdges__(wire.Edges)) sh = DraftGeomUtils.bind(w1,w2) elif obj.Align == "Center": @@ -914,10 +936,13 @@ class _Wall(ArchComponent.Component): d1 = Vector(dvec).multiply(off) w2 = DraftGeomUtils.offsetWire(wire,d1) else: - dvec.multiply(width/2) - w1 = DraftGeomUtils.offsetWire(wire,dvec) + dvec.multiply(width/2) ## TODO width Value should be of no use (width/2), width Direction remains 'in use' + + widthsOfWallHalfen = [i/2 for i in obj.WidthsOfWall] + w1 = DraftGeomUtils.offsetWire(wire,dvec,False, False, widthsOfWallHalfen) dvec = dvec.negative() - w2 = DraftGeomUtils.offsetWire(wire,dvec) + w2 = DraftGeomUtils.offsetWire(wire,dvec,False, False, widthsOfWallHalfen) + sh = DraftGeomUtils.bind(w1,w2) if sh: sh.fix(0.1,0,1) # fixes self-intersecting wires diff --git a/src/Mod/Draft/DraftGeomUtils.py b/src/Mod/Draft/DraftGeomUtils.py index 9a1ba5ddc7..4f4638deb5 100644 --- a/src/Mod/Draft/DraftGeomUtils.py +++ b/src/Mod/Draft/DraftGeomUtils.py @@ -1171,14 +1171,24 @@ def calculatePlacement(shape): pla.Rotation = r return pla -def offsetWire(wire,dvec,bind=False,occ=False): +def offsetWire(wire,dvec,bind=False,occ=False,widthList=None): ''' offsetWire(wire,vector,[bind]): offsets the given wire along the given vector. The vector will be applied at the first vertex of the wire. If bind is True (and the shape is open), the original wire and the offsetted one are bound by 2 edges, forming a face. + + If widthList is provided (values only, not lengths - i.e. no unit), + each value will be used to offset each corresponding edge in the wire + + (The 1st value override 'dvec' for 1st segement of wire; + if a value is zero, value of 'widthList[0]' will follow; + if widthList[0]' == 0, but dvec still provided, dvec will be followed) ''' + ## TODO In future, 'vector' direction to offset could be 'calculated' in this function - if 'direction' in dvec is not / need not be provided 'outside' the function + ## 'dvec' to be obsolete in future ? + edges = wire.Edges # Seems has repeatedly sortEdges, remark out here - edges = Part.__sortEdges__(wire.Edges) norm = getNormal(wire) closed = isReallyClosed(wire) @@ -1206,14 +1216,39 @@ def offsetWire(wire,dvec,bind=False,occ=False): for i in range(len(edges)): curredge = edges[i] - delta = dvec + + if widthList: + try: + if widthList[i] > 0: + delta = DraftVecUtils.scaleTo(dvec, widthList[i]) + elif widthList[0] > 0: + delta = DraftVecUtils.scaleTo(dvec, widthList[0]) # to follow widthList[0] + + # i.e. if widthList[0] == 0, though widthList is not False + # but if dev is provided still, fallback to dvec + elif dvec: + delta = dvec + + else: + return None + except: + if widthList[0] > 0: + delta = DraftVecUtils.scaleTo(dvec, widthList[0]) # to follow widthList[0] + delta = dvec + else: + delta = dvec + if i != 0: if isinstance(curredge.Curve,Part.Circle): v = curredge.tangentAt(curredge.FirstParameter) else: v = vec(curredge) + + ## TODO - 2019.6.16 - 'calculate' 'offset' direction (in vector) edge by edge instead of rotating previous vector based on dvec in future + angle = DraftVecUtils.angle(firstVec,v,norm) # use vec deduced depending on geometry instead of - angle = DraftVecUtils.angle(vec(edges[0]),v,norm) delta = DraftVecUtils.rotate(delta,angle,norm) + #print("edge ",i,": ",curredge.Curve," ",curredge.Orientation," parameters:",curredge.ParameterRange," vector:",delta) nedge = offset(curredge,delta,trim=True) if not nedge: @@ -1259,16 +1294,23 @@ def connect(edges,closed=False): if prev: #print("debug: DraftGeomUtils.connect prev : ",prev.Vertexes[0].Point,prev.Vertexes[-1].Point) - # If prev v2 had been calculated, do not calculate again, just use it as current v1 - avoid chance of slight difference in result - if v2: - v1 = v2 + # If the edge pairs has intersection + # ... and if there is prev v2 (prev v2 was caculated intersection), do not calculate again, just use it as current v1 - avoid chance of slight difference in result + # Otherwise, if edge pairs has no intersection (parallel edges, line - arc do no intersect, etc.), so just just current edge endpoints as v1 + # ... and connect these 2 non-intersecting edges - else: - i = findIntersection(curr,prev,True,True) - if i: + # seem have chance that 2 parallel edges offset same width, result in 2 colinear edges - Wall / DraftGeomUtils seem make them 1 edge and thus 1 vertical plane + i = findIntersection(curr,prev,True,True) + if i: + if v2: + v1 = v2 + else: v1 = i[DraftVecUtils.closest(curr.Vertexes[0].Point,i)] - else: + else: v1 = curr.Vertexes[0].Point + + nedges.append(Part.LineSegment(v2,v1).toShape()) + else: v1 = curr.Vertexes[0].Point if next: