MEPover package MEPFitting.ByPointsAndCurve node

i try to use that node to insert valve in the middle of pipe but unsuccessful, related Python node is extracted and displayed error message so i tried list as input but same error message, i may miss something simple, any insight?

The error is saying the code is trying to iterate over something that is not iterable. I believe this is due to your points. The node seems to be asking for a list of points and you’re providing a singular structure. Just wrap your point in a list and it should work.

thanks Nick, that’s what i did, see below updated, same error message

Hi,
MEPover has not been updated to CPython3 (or IronPython3 or PythonNet3).
You must:

  • either update the Python code
  • or use another package such as OpenMEP
  • or use IronPython2 engine (as a last resort)
2 Likes

thanks c.poupin, both IronPython2 and CPython3 won’t work.

Yeah as @c.poupin say, mepover isnt upgraded for cpython 3, but guess your warning say "iteration over non sequence, try input it as list and could probably work or try this one here, should work with cpython 3
Revit_PuJ1YrUsR8

import clr
import math
import traceback
clr.AddReference("RevitAPI")
clr.AddReference("RevitServices")
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
from Autodesk.Revit.DB import *
doc = DocumentManager.Instance.CurrentDBDocument
import Revit
clr.ImportExtensions(Revit.Elements)
clr.ImportExtensions(Revit.GeometryConversion)
from System.Collections.Generic import List

ducts = UnwrapElement(IN[0]) if isinstance(IN[0], list) else [UnwrapElement(IN[0])]
points = IN[1] if isinstance(IN[1], list) else [IN[1]]
famtype = UnwrapElement(IN[2]) if isinstance(IN[2], list) else [UnwrapElement(IN[2])]
ftl = len(famtype)

def IsParallel(dir1, dir2):
    if dir1.Normalize().IsAlmostEqualTo(dir2.Normalize()):
        return True
    if dir1.Normalize().Negate().IsAlmostEqualTo(dir2.Normalize()):
        return True
    return False

def measure(startpoint, point):
    return startpoint.DistanceTo(point)


def copyElement(element, oldloc, loc):
    elementlist = List[ElementId]()
    elementlist.Add(element.Id)
    OffsetZ = (oldloc.Z - loc.Z) * -1
    OffsetX = (oldloc.X - loc.X) * -1
    OffsetY = (oldloc.Y - loc.Y) * -1
    direction = XYZ(OffsetX, OffsetY, OffsetZ)
    newelementId = ElementTransformUtils.CopyElements(doc, elementlist, direction)
    newelement = doc.GetElement(newelementId[0])
    return newelement


def GetDirection(faminstance):
    for c in faminstance.MEPModel.ConnectorManager.Connectors:
        conn = c
        break
    return conn.CoordinateSystem.BasisZ


def GetClosestDirection(faminstance, lineDirection):
    flat_linedir = XYZ(lineDirection.X, lineDirection.Y, 0).Normalize()
    for conn in faminstance.MEPModel.ConnectorManager.Connectors:
        conndir = conn.CoordinateSystem.BasisZ
        if flat_linedir.IsAlmostEqualTo(conndir):
            return conndir
    return None


tempfamtype = None
xAxis = XYZ(1, 0, 0)


def placeFitting(duct, point, familytype, lineDirection):
    global tempfamtype
    toggle = False
    isVertical = False

    if tempfamtype is None:
        tempfamtype = familytype
        toggle = True
    elif tempfamtype != familytype:
        toggle = True
        tempfamtype = familytype

    level = duct.ReferenceLevel

    width = 4
    height = 4
    round = False
    connectors = duct.ConnectorManager.Connectors
    for c in connectors:
        if c.ConnectorType != ConnectorType.End:
            continue
        shape = c.Shape
        if shape == ConnectorProfileType.Round:
            round = True
            break
        elif shape == ConnectorProfileType.Rectangular or shape == ConnectorProfileType.Oval:
            if abs(lineDirection.Z) == 1:
                isVertical = True
                yDir = c.CoordinateSystem.BasisY
            width = c.Width
            height = c.Height
            break

    point = XYZ(point.X, point.Y, point.Z - level.Elevation)
    newfam = doc.Create.NewFamilyInstance(point, familytype, level, Structure.StructuralType.NonStructural)
    doc.Regenerate()
    transform = newfam.GetTransform()
    axis = Line.CreateUnbound(transform.Origin, transform.BasisZ)

    global xAxis
    if toggle:
        xAxis = GetDirection(newfam)
    zAxis = XYZ(0, 0, 1)

    if isVertical:
        angle = xAxis.AngleOnPlaneTo(yDir, zAxis)
    else:
        angle = xAxis.AngleOnPlaneTo(lineDirection, zAxis)

    ElementTransformUtils.RotateElement(doc, newfam.Id, axis, angle)
    doc.Regenerate()

    if lineDirection.Z != 0:
        newAxis = GetClosestDirection(newfam, lineDirection)
        yAxis = newAxis.CrossProduct(zAxis)
        angle2 = newAxis.AngleOnPlaneTo(lineDirection, yAxis)
        axis2 = Line.CreateUnbound(transform.Origin, yAxis)
        ElementTransformUtils.RotateElement(doc, newfam.Id, axis2, angle2)

    result = {}
    connpoints = []
    famconns = newfam.MEPModel.ConnectorManager.Connectors

    for conn in famconns:
        if IsParallel(lineDirection, conn.CoordinateSystem.BasisZ):
            connpoints.append(conn.Origin)

    result[newfam] = connpoints
    return result


def ConnectElements(duct, fitting):
    ductconns = duct.ConnectorManager.Connectors
    fittingconns = fitting.MEPModel.ConnectorManager.Connectors
    for conn in fittingconns:
        for ductconn in ductconns:
            if ductconn.Origin.IsAlmostEqualTo(conn.Origin):
                ductconn.ConnectTo(conn)
                break


def SortedPoints(fittingspoints, ductStartPoint):
    return sorted(fittingspoints, key=lambda x: measure(ductStartPoint, x))


ductsout = []
fittingsout = []
combilist = []

TransactionManager.Instance.EnsureInTransaction(doc)


for typ in famtype:
    if not typ.IsActive:
        typ.Activate()
        doc.Regenerate()


for i, duct in enumerate(ducts):
    ListOfPoints = [x.ToXyz() for x in points[i]]
    familytype = famtype[i % ftl]
    ductline = duct.Location.Curve
    ductStartPoint = ductline.GetEndPoint(0)
    ductEndPoint = ductline.GetEndPoint(1)

    pointlist = SortedPoints(ListOfPoints, ductStartPoint)

    ductlist = []
    newFittings = []
    ductlist.append(duct)
    tempStartPoint = None
    tempEndPoint = None

    lineDirection = ductline.Direction

    for i, p in enumerate(pointlist):
        output = placeFitting(duct, p, familytype, lineDirection)
        newfitting = list(output.keys())[0]
        newFittings.append(newfitting)
        fittingpoints = list(output.values())[0]

        tempPoints = SortedPoints(fittingpoints, ductStartPoint)
        if i == 0:
            tempEndPoint = tempPoints[0]
            tempStartPoint = tempPoints[1]
            newduct = copyElement(duct, ductStartPoint, tempStartPoint)
            duct.Location.Curve = Line.CreateBound(ductStartPoint, tempEndPoint)
            ductlist.append(newduct)
            combilist.append([duct, newfitting])
            combilist.append([newduct, newfitting])
        else:
            combilist.append([newduct, newfitting])
            tempEndPoint = tempPoints[0]
            newduct = copyElement(duct, tempStartPoint, tempEndPoint)
            ductlist[-1].Location.Curve = Line.CreateBound(tempStartPoint, tempEndPoint)
            tempStartPoint = tempPoints[1]
            ductlist.append(newduct)
            combilist.append([newduct, newfitting])

    ductlist[-1].Location.Curve = Line.CreateBound(tempStartPoint, ductEndPoint)

    ductsout.append(ductlist)
    fittingsout.append(newFittings)

TransactionManager.Instance.TransactionTaskDone()


TransactionManager.Instance.EnsureInTransaction(doc)
for combi in combilist:
    ConnectElements(combi[0], combi[1])
TransactionManager.Instance.TransactionTaskDone()

OUT = (ductsout, fittingsout)



1 Like

In addition to @sovitek 's answer, It seems to me that the list of points should be a list of lists (one sublist per duct/pipe)

3 Likes

@c.poupin yes :wink:

2 Likes

and you could try this one here before feed in your points, then it should work for both single and multiple points, and dont need list.create and 2 x list create for single point..

data = IN[0]

if not isinstance(data, list):
    data = [[data]]
elif not isinstance(data[0], list):
    data = [data]

OUT = data

1 Like

thanks sovitek and c.poupin, list of list!

2 Likes

Hi,

Here is a revised version with IronPython3 or PythonNet3 (probably works with IronPython2, but untested).

Python Code
import clr
import sys
import System
from System.Collections.Generic import List
#
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
import Autodesk.DesignScript.Geometry as DS

#import Revit API
clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import *
import Autodesk.Revit.DB as DB
#import specify namespace
from Autodesk.Revit.DB.Plumbing import *
from Autodesk.Revit.DB.Mechanical import *
from Autodesk.Revit.DB.Structure import *

clr.AddReference('RevitNodes')
import Revit
clr.ImportExtensions(Revit.GeometryConversion)

clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
doc = DocumentManager.Instance.CurrentDBDocument

import math

def get_shape(lst, shape=()):
    """
    returns the shape of nested lists 
    """
    if not hasattr(lst, "__iter__") or isinstance(lst, (str, System.String)):
        # base case
        return {"shape":shape, "ndim":len(shape)}
    if hasattr(lst[0], "__iter__") and not isinstance(lst[0], (str, System.String)):
        l = len(lst[0])
    shape += (len(lst), )
    # recurse
    shape = get_shape(lst[0], shape)
    return shape #{"shape":shape, "ndim":len(shape)} 

def get_order_connectors(elemA, elemB, elemC):
    test = elemA
    lst = [elemA, elemB, elemC]
    # create group by direction
    groupA = [i for i in lst if abs(test.Location.Curve.Direction.CrossProduct(i.Location.Curve.Direction).Z) < 0.001]
    groupB = [i for i in lst if abs(test.Location.Curve.Direction.CrossProduct(i.Location.Curve.Direction).Z) > 0.001]
    lstGroup = [groupA, groupB]
    # sort list of group by len 
    lstGroup.sort(key = len, reverse = True)
    # flat the group : the last element is the branch
    lst_sort_elem = sum(lstGroup, [])
    lst_curve_elems = [i.Location.Curve for i in lst_sort_elem]
    orderConnectors = []
    # get closest connectors
    for idxA, elem in enumerate(lst_sort_elem):
        other_curve = [c for idxB, c in enumerate(lst_curve_elems) if idxA != idxB][0]
        print(other_curve)
        closest_connectors = sorted(elem.ConnectorManager.Connectors, key = lambda c :  other_curve.Project(c.Origin).Distance)
        orderConnectors.append(closest_connectors[0])
    #
    return orderConnectors

def get_FamilyInstance_direction(elem):
    connectors = [c for c in elem.MEPModel.ConnectorManager.Connectors if c.Domain in [Domain.DomainPiping, Domain.DomainHvac]]
    i = 0
    groups_arr = [ [connectors.pop(0)] ]
    while connectors and i < 10:
        i += 1
        con = connectors.pop(0)
        is_placed = False
        for idx, group in enumerate(groups_arr):
            # if connector CoordinateSystem is parrallel add to current group
            if con.CoordinateSystem.BasisZ.CrossProduct(group[-1].CoordinateSystem.BasisZ).GetLength() < 0.02:
                group.append(con)
                is_placed = True
        if not is_placed :
            groups_arr.append([con])
    #
    print(f"{groups_arr=}")
    main_connectors = next((g for g in groups_arr if len(g) == 2), None)
    if main_connectors is not None:
        pta, ptb = sorted([c.Origin for c in main_connectors], key = lambda p : p.X)
        pta, ptb = [c.Origin for c in main_connectors]
        return DB.Line.CreateBound(pta, ptb).Direction.Normalize(), main_connectors
    return None, []

def toList(x):
    if isinstance(x, (list, dict)) or \
            (hasattr(x, "GetType") and x.GetType().GetInterface("ICollection") is not None):
        return x
    else : return [x]

#Preparing input from dynamo to revit
lst_elemCurves = [x for x in toList(UnwrapElement(IN[0])) if isinstance(x.Location, LocationCurve)]
lst_elemCurveIds = [x.Id for x in lst_elemCurves]
arr_arr_DSPoints = toList(IN[1])
arr_arr_DSPoints[0] = toList(arr_arr_DSPoints[0])
#
# check if data input structure is correct
shape_Elem = get_shape(lst_elemCurves)
shape_DSPoints = get_shape(arr_arr_DSPoints)
# 
if shape_Elem["ndim"] == 1\
            and shape_DSPoints["ndim"] == 2\
            and  shape_Elem["shape"][0] == shape_DSPoints["shape"][0]:
    pass
else:
    raise Exception("wrong list input structure")
    
fitting_type = UnwrapElement(IN[2])
out_fitting = []
debug = []
#
is_tee = fitting_type.Family.get_Parameter(BuiltInParameter.FAMILY_CONTENT_PART_TYPE).AsInteger() == 6
#Do some action in a Transaction
TransactionManager.Instance.EnsureInTransaction(doc)
for array_DSPts, elem in zip(arr_arr_DSPoints, lst_elemCurves):
    # get datas from Curve 
    c_system = list(elem.ConnectorManager.Connectors)[0].CoordinateSystem
    curve_elem = elem.Location.Curve.Clone()
    curve_direction = curve_elem.Direction.Normalize()
    level = doc.GetElement(elem.LevelId)
    #
    array_pts = [p.ToXyz() for p in array_DSPts]
    array_pts.sort(key = lambda p : p.DistanceTo(curve_elem.GetEndPoint(0)) )
    elemId = elem.Id
    #
    for pt in array_pts:
        newfam = None
        try:
            newId = MechanicalUtils.BreakCurve(doc, elemId, pt)
        except Exception as ex:
            print(ex)
            newId = PlumbingUtils.BreakCurve(doc, elemId, pt)
        #
        doc.Regenerate()
        elemA = doc.GetElement(elemId)
        curveA = elemA.Location.Curve
        elemB = doc.GetElement(newId)
        curveB = elemB.Location.Curve
        try :
            v_axis = curve_direction.CrossProduct(XYZ.BasisX)
            axis = Line.CreateUnbound(pt, v_axis)
        except:
            v_axis = curve_direction.CrossProduct(XYZ.BasisY)
            axis = Line.CreateUnbound(pt, v_axis)
        # tee creation
        if is_tee :
            # create temapary the third element (branch)
            elemCId = list(DB.ElementTransformUtils.CopyElement(doc, elemA.Id, XYZ.Zero))[0]
            elemC = doc.GetElement(elemCId)
            # rotate if verticale
            if abs(c_system.BasisZ.Z) > 0.99:
                axis = Line.CreateUnbound(pt, c_system.BasisY)
                DB.ElementTransformUtils.RotateElement(doc, elemCId, axis, math.pi / 2)
            else:
                DB.ElementTransformUtils.RotateElement(doc, elemCId, axis, math.pi / 2)
            #
            doc.Regenerate()
            conA, conB, conC = get_order_connectors(elemA, elemB, elemC)
            newfam = doc.Create.NewTeeFitting(conA, conB, conC)
            newfam.ChangeTypeId(fitting_type.Id)
            out_fitting.append(newfam)
            # delete the temp branch
            doc.Delete(elemCId)
        #
        # accessory creation
        else:
            if not fitting_type.IsActive:
                fitting_type.Activate()
            newfam = doc.Create.NewFamilyInstance(pt, fitting_type, level, StructuralType.NonStructural)
            out_fitting.append(newfam)
            doc.Regenerate()
            # get direction of familyInstance by connectors position
            v1, connectors = get_FamilyInstance_direction(newfam)
            print(f"v1={v1.ToString()}")
            if v1 is not None:
                # check if vectors are Clockwise
                factor = 1 if v1.CrossProduct(curve_direction).Z > 0.01 else -1
                angle = v1.AngleTo(curve_direction)
                # align direction of familyinstance
                ## rotate if verticale
                if abs(c_system.BasisZ.Z) > 0.99 :
                    DB.ElementTransformUtils.RotateElement(doc, newfam.Id, axis, factor * angle )
                    factorX = 1 if c_system.BasisX.CrossProduct(XYZ.BasisX).Z < 0 else -1
                    DB.ElementTransformUtils.RotateElement(doc, newfam.Id, curve_elem, factorX * c_system.BasisX.AngleTo(XYZ.BasisX))
                ## if horizontal redifine the axis 
                else:
                    axis = Line.CreateBound(pt, pt + XYZ(0,0,1))
                    DB.ElementTransformUtils.RotateElement(doc, newfam.Id, axis, factor * angle )
                #
                doc.Regenerate()
                # connect connectors to pipes/ducts
                for con in connectors:
                    for curv_, elem_ in [[curveA, elemA], [curveB, elemB]]:
                        interR = curv_.Project(con.Origin)
                        #print(curv_.ComputeNormalizedParameter(interR.Parameter))
                        #
                        if interR is not None and 0.005 < curv_.ComputeNormalizedParameter(interR.Parameter) < 0.995:
                            closest_connectors = sorted(elem_.ConnectorManager.Connectors, key = lambda c :  c.Origin.DistanceTo(con.Origin))
                            closest_connector = closest_connectors[0]
                            closest_connector.ConnectTo(con)
                            closest_connector.Origin = con.Origin


TransactionManager.Instance.TransactionTaskDone()
OUT = lst_elemCurves, out_fitting
2 Likes