mirror of
https://github.com/jeremytammik/RevitSdkSamples.git
synced 2026-09-19 18:48:48 +00:00
396 lines
13 KiB
C#
396 lines
13 KiB
C#
//
|
|
// (C) Copyright 2003-2019 by Autodesk, Inc.
|
|
//
|
|
// Permission to use, copy, modify, and distribute this software in
|
|
// object code form for any purpose and without fee is hereby granted,
|
|
// provided that the above copyright notice appears in all copies and
|
|
// that both that copyright notice and the limited warranty and
|
|
// restricted rights notice below appear in all supporting
|
|
// documentation.
|
|
//
|
|
// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
|
|
// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
|
|
// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
|
|
// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
|
|
// UNINTERRUPTED OR ERROR FREE.
|
|
//
|
|
// Use, duplication, or disclosure by the U.S. Government is subject to
|
|
// restrictions set forth in FAR 52.227-19 (Commercial Computer
|
|
// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
|
|
// (Rights in Technical Data and Computer Software), as applicable.
|
|
//
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
|
|
using Autodesk.Revit;
|
|
using Autodesk.Revit.DB;
|
|
using Autodesk.Revit.UI.Selection;
|
|
using Revit.SDK.Samples.CurtainSystem.CS.Data;
|
|
|
|
namespace Revit.SDK.Samples.CurtainSystem.CS.CurtainSystem
|
|
{
|
|
/// <summary>
|
|
/// check whether the selected element is a mass and whether the mass is kind of parallelepiped
|
|
/// (only mass of parallelepiped supported in this sample)
|
|
/// </summary>
|
|
class MassChecker
|
|
{
|
|
// the document containing all the data used in the sample
|
|
MyDocument m_mydocument;
|
|
|
|
/// <summary>
|
|
/// Constructor
|
|
/// </summary>
|
|
/// <param name="mydoc">
|
|
/// the document of the sample
|
|
/// </param>
|
|
public MassChecker(MyDocument mydoc)
|
|
{
|
|
m_mydocument = mydoc;
|
|
}
|
|
|
|
/// <summary>
|
|
/// check whether the selection is a parallelepiped mass
|
|
/// </summary>
|
|
public bool CheckSelectedMass()
|
|
{
|
|
// get the selected mass
|
|
FamilyInstance mass = GetSelectedMass();
|
|
// start the sample without making a parallelepiped mass selected
|
|
if (null == mass)
|
|
{
|
|
m_mydocument.FatalErrorMsg = Properties.Resources.MSG_InvalidSelection;
|
|
return false;
|
|
}
|
|
|
|
// check whether the mass is parallelepiped
|
|
bool isMassParallelepiped = IsMassParallelepiped(mass);
|
|
if (false == isMassParallelepiped)
|
|
{
|
|
m_mydocument.FatalErrorMsg = Properties.Resources.MSG_InvalidSelection;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// check whether the mass is a parallelepiped mass by checking the faces' normals
|
|
/// (if it's a parallelepiped mass, it will have 3 groups of parallel faces)
|
|
/// </summary>
|
|
/// <param name="mass">
|
|
/// the mass to be checked
|
|
/// </param>
|
|
/// <returns>
|
|
/// return true if the mass is parallelepiped; otherwise false
|
|
/// </returns>
|
|
private bool IsMassParallelepiped(FamilyInstance mass)
|
|
{
|
|
FaceArray faces = GetMassFaceArray(mass);
|
|
|
|
// a parallelepiped always has 6 faces
|
|
if (null == faces ||
|
|
6 != faces.Size)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool isFacesParallel = IsFacesParallel(faces);
|
|
|
|
// store the face array
|
|
if (true == isFacesParallel)
|
|
{
|
|
m_mydocument.MassFaceArray = faces;
|
|
}
|
|
|
|
return isFacesParallel;
|
|
}
|
|
|
|
/// <summary>
|
|
/// check whether the faces of the mass are one-one parallel
|
|
/// </summary>
|
|
/// <param name="faces">
|
|
/// the 6 faces of the mass
|
|
/// </param>
|
|
/// <returns>
|
|
/// if the 6 faces are one-one parallel, return true; otherwise false
|
|
/// </returns>
|
|
private bool IsFacesParallel(FaceArray faces)
|
|
{
|
|
// step1: get the normals of the 6 faces
|
|
List<Utility.Vector4> normals = new List<Utility.Vector4>();
|
|
foreach (Face face in faces)
|
|
{
|
|
EdgeArrayArray edgeArrayArray = face.EdgeLoops;
|
|
EdgeArray edges = edgeArrayArray.get_Item(0);
|
|
|
|
if (null == edges ||
|
|
2 > edges.Size)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// we use the cross product of 2 non-parallel vectors as the normal
|
|
for (int i = 0; i < edges.Size - 1; i++)
|
|
{
|
|
Edge edgeA = edges.get_Item(i);
|
|
Edge edgeB = edges.get_Item(i + 1);
|
|
|
|
// if edgeA & edgeB are parallel, can't compute the cross product
|
|
bool isLinesParallel = IsLinesParallel(edgeA, edgeB);
|
|
|
|
if (true == isLinesParallel)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
Utility.Vector4 vec4 = ComputeCrossProduct(edgeA, edgeB);
|
|
normals.Add(vec4);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// step 2: the 6 normals should be one-one parallel pairs
|
|
if (null == normals ||
|
|
6 != normals.Count)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool[] matchedList = new bool[6];
|
|
for (int i = 0; i < matchedList.Length; i++)
|
|
{
|
|
matchedList[i] = false;
|
|
}
|
|
|
|
// check whether the normal has another matched parallel normal
|
|
for (int i = 0; i < matchedList.Length; i++)
|
|
{
|
|
if (true == matchedList[i])
|
|
{
|
|
continue;
|
|
}
|
|
|
|
Utility.Vector4 vec4A = normals[i];
|
|
|
|
for (int j = 0; j < matchedList.Length; j++)
|
|
{
|
|
if (j == i ||
|
|
true == matchedList[j])
|
|
{
|
|
continue;
|
|
}
|
|
|
|
Utility.Vector4 vec4B = normals[j];
|
|
|
|
if (true == IsLinesParallel(vec4A, vec4B))
|
|
{
|
|
matchedList[i] = true;
|
|
matchedList[j] = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// step 3: check each of the 6 normals has matched parallel normal
|
|
for (int i = 0; i < matchedList.Length; i++)
|
|
{
|
|
if (false == matchedList[i])
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// all the normals have matched parallel normals
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// check whether 2 edges are parallel
|
|
/// </summary>
|
|
/// <param name="edgeA">
|
|
/// the edge to be checked
|
|
/// </param>
|
|
/// <param name="edgeB">
|
|
/// the edge to be checked
|
|
/// </param>
|
|
/// <returns>
|
|
/// if they're parallel, return true; otherwise false
|
|
/// </returns>
|
|
private bool IsLinesParallel(Edge edgeA, Edge edgeB)
|
|
{
|
|
List<XYZ> pointsA = edgeA.Tessellate() as List<XYZ>;
|
|
List<XYZ> pointsB = edgeB.Tessellate() as List<XYZ>;
|
|
Autodesk.Revit.DB.XYZ vectorA = pointsA[1] - pointsA[0];
|
|
Autodesk.Revit.DB.XYZ vectorB = pointsB[1] - pointsB[0];
|
|
Utility.Vector4 vec4A = new Utility.Vector4(vectorA);
|
|
Utility.Vector4 vec4B = new Utility.Vector4(vectorB);
|
|
return IsLinesParallel(vec4A, vec4B);
|
|
}
|
|
|
|
/// <summary>
|
|
/// check whether 2 vectors are parallel
|
|
/// </summary>
|
|
/// <param name="vec4A">
|
|
/// the vector to be checked
|
|
/// </param>
|
|
/// <param name="vec4B">
|
|
/// the vector to be checked
|
|
/// </param>
|
|
/// <returns>
|
|
/// if they're parallel, return true; otherwise false
|
|
/// </returns>
|
|
private bool IsLinesParallel(Utility.Vector4 vec4A, Utility.Vector4 vec4B)
|
|
{
|
|
// if 2 vectors are parallel, they should be like the following formula:
|
|
// vec4A.X vec4A.Y vec4A.Z
|
|
// ------- == ------- == -------
|
|
// vec4B.X vec4B.Y vec4B.Z
|
|
// change to multiply, it's
|
|
// vec4A.X * vec4B.Y == vec4A.Y * vec4B.X &&
|
|
// vec4A.Y * vec4B.Z == vec4A.Z * vec4B.Y
|
|
double aa = vec4A.X * vec4B.Y;
|
|
double bb = vec4A.Y * vec4B.X;
|
|
double cc = vec4A.Y * vec4B.Z;
|
|
double dd = vec4A.Z * vec4B.Y;
|
|
double ee = vec4A.X * vec4B.Z;
|
|
double ff = vec4A.Z * vec4B.X;
|
|
|
|
const double tolerance = 0.0001d;
|
|
|
|
if (Math.Abs(aa - bb) < tolerance &&
|
|
Math.Abs(cc - dd) < tolerance &&
|
|
Math.Abs(ee - ff) < tolerance)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// compute the cross product of 2 edges
|
|
/// </summary>
|
|
/// <param name="edgeA">
|
|
/// the edge for the cross product
|
|
/// </param>
|
|
/// <param name="edgeB">
|
|
/// the edge for the cross product
|
|
/// </param>
|
|
/// <returns>
|
|
/// the cross product of 2 edges
|
|
/// </returns>
|
|
private Utility.Vector4 ComputeCrossProduct(Edge edgeA, Edge edgeB)
|
|
{
|
|
List<XYZ> pointsA = edgeA.Tessellate() as List<XYZ>;
|
|
List<XYZ> pointsB = edgeB.Tessellate() as List<XYZ>;
|
|
Autodesk.Revit.DB.XYZ vectorA = pointsA[1] - pointsA[0];
|
|
Autodesk.Revit.DB.XYZ vectorB = pointsB[1] - pointsB[0];
|
|
Utility.Vector4 vec4A = new Utility.Vector4(vectorA);
|
|
Utility.Vector4 vec4B = new Utility.Vector4(vectorB);
|
|
return Utility.Vector4.CrossProduct(vec4A, vec4B);
|
|
}
|
|
|
|
/// <summary>
|
|
/// get the faces of the mass
|
|
/// </summary>
|
|
/// <param name="mass">
|
|
/// the source mass
|
|
/// </param>
|
|
/// <returns>
|
|
/// the faces of the mass
|
|
/// </returns>
|
|
private FaceArray GetMassFaceArray(FamilyInstance mass)
|
|
{
|
|
// Obtain the gemotry information of the mass
|
|
Autodesk.Revit.DB.Options opt = m_mydocument.CommandData.Application.Application.Create.NewGeometryOptions();
|
|
opt.DetailLevel = ViewDetailLevel.Fine;
|
|
opt.ComputeReferences = true;
|
|
Autodesk.Revit.DB.GeometryElement geoElement = null;
|
|
try
|
|
{
|
|
geoElement = mass.get_Geometry(opt);
|
|
}
|
|
catch (System.Exception)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
if (null == geoElement)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
//GeometryObjectArray objectarray = geoElement.Objects;
|
|
//foreach (GeometryObject obj in objectarray)
|
|
IEnumerator<GeometryObject> Objects = geoElement.GetEnumerator();
|
|
while (Objects.MoveNext())
|
|
{
|
|
GeometryObject obj = Objects.Current;
|
|
|
|
Solid solid = obj as Solid;
|
|
|
|
if (null != solid &&
|
|
null != solid.Faces &&
|
|
0 != solid.Faces.Size)
|
|
{
|
|
return solid.Faces;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// get the selected mass
|
|
/// </summary>
|
|
/// <returns>
|
|
/// return the selected mass; if it's not a mass, return null
|
|
/// </returns>
|
|
private FamilyInstance GetSelectedMass()
|
|
{
|
|
FamilyInstance resultMass = null;
|
|
|
|
// check whether a mass was selected before launching this sample
|
|
Selection selection = m_mydocument.UIDocument.Selection;
|
|
ElementSet elementSet = new ElementSet();
|
|
foreach (ElementId elementId in selection.GetElementIds())
|
|
{
|
|
elementSet.Insert(m_mydocument.UIDocument.Document.GetElement(elementId));
|
|
}
|
|
if (null == selection ||
|
|
null == elementSet ||
|
|
true == elementSet.IsEmpty ||
|
|
1 != elementSet.Size)
|
|
{
|
|
//m_mydocument.FatalErrorMsg = Properties.Resources.MSG_InvalidSelection;
|
|
return null;
|
|
}
|
|
|
|
foreach (Autodesk.Revit.DB.ElementId selElementId in selection.GetElementIds())
|
|
{
|
|
Autodesk.Revit.DB.Element selElement = m_mydocument.UIDocument.Document.GetElement(selElementId);
|
|
FamilyInstance inst = selElement as FamilyInstance;
|
|
if (null != inst &&
|
|
"Mass" == inst.Category.Name)
|
|
{
|
|
resultMass = inst;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// nothing selected or the selected element is not a mass
|
|
if (null == resultMass)
|
|
{
|
|
//m_mydocument.FatalErrorMsg = Properties.Resources.MSG_InvalidSelection;
|
|
return null;
|
|
}
|
|
|
|
return resultMass;
|
|
}
|
|
|
|
}
|
|
}
|