/
Edwar
/
cam-api-examples
Обзор
Документация
Войти
/
Edwar
/
cam-api-examples
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
Geometry/ExtensionUtilityGeomCustomImportNet/project/main/ExtensionImportSvg.cs
540 строк
20 KB
Edwar
Импортер 3д моделей из Obj файлов
03 июн 2025, 20:15
03 июн 2025, 20:15
697c197
Код
Авторство
О чём код?
using System.IO; using System.Xml; using System.Text.RegularExpressions; using System.Reflection.Metadata; using CAMAPI.Application; using CAMAPI.DotnetHelper; using CAMAPI.Extensions; using CAMAPI.ResultStatus; using CAMAPI.Singletons; using Geometry.VecMatrLib; using STGeomApiTypes; using STTypes; using System.Globalization; namespace ExtensionUtilityGeomCustomImportNet; /// <summary> /// Utility to import svg file /// </summary> public class ExtensionImportSvg : IExtension, IExtensionUtility { /// <inheritdoc /> public IExtensionInfo? Info { get; set; } //private ISTGeomFiler? geomFile; readonly RegexOptions rxOpt = RegexOptions.Singleline | RegexOptions.IgnorePatternWhitespace | RegexOptions.CultureInvariant; readonly float precision = .1F; private static double Attribute2Double(XmlNode node, string str) { return node.Attributes[str] == null ? 0 : double.Parse(node.Attributes[str].Value, CultureInfo.InvariantCulture); } /* private static double Attribute2Double(XmlElement element, string str) { return element.GetAttribute(str) == "" ? 0 : double.Parse(element.GetAttribute(str), CultureInfo.InvariantCulture); } */ bool DrawPath(XmlNode element, ISTGeomFiler geomFile ) { Console.Write("DrawPath\n"); //Console.Write("found path: {0}\n", element.Name); string pathName = element.Name; //element.Name; Console.Write("Found a path: {0}\n", pathName); MatchCollection polygonValuesMatches = Regex.Matches(element.Attributes["d"].Value, @"([mMzZlLhHvVcCsSqQtTaA])([^mMzZlLhHvVcCsSqQtTaA]*)", rxOpt); bool upperCase = false; bool initialPos = false; bool closed = false; //TST2DPoint InitialCursorPos = new() { X = 0, Y = 0 }; TST3DPoint CurrentCursorPos = new() { X = 0, Y = 0, Z = 0 }; //double[] CurrentCursorPos = [0, 0]; foreach (Match m in polygonValuesMatches) { //char func = m.Groups[1].Value[0]; List<double> values = []; //double[] values = []; //Console.Write("{0}\n", m.Groups[1].Value);//double.Parse(m.Groups[2].Value, CultureInfo.InvariantCulture)); if (m.Groups[2].Value != "") { MatchCollection valueMatches = Regex.Matches(m.Groups[2].Value, @"-?\d*\.?\d+", rxOpt); foreach (Match valueMatch in valueMatches) { values.Add(double.Parse(valueMatch.Value, CultureInfo.InvariantCulture)); //Console.Write("{0} ", double.Parse(valueMatch.Value, CultureInfo.InvariantCulture)); } //Console.Write("\n"); //Console.Write(values.Count%2); } upperCase = char.IsUpper(m.Groups[1].Value[0]); closed = false; switch (m.Groups[1].Value[0]) { case 'M' or 'm': if (upperCase || !initialPos) { //CurrentCursorPos = [.. values[i..1]];//[values[i], values[i + 1]]; CurrentCursorPos.X = values[0]; CurrentCursorPos.Y = values[1]; initialPos = true; //InitialCursorPos = CurrentCursorPos; //Console.Write(CurrentCursorPos); } else { CurrentCursorPos.X += values[0]; CurrentCursorPos.Y += values[1]; } geomFile.StartCurve3d( pathName, CurrentCursorPos ); for (int i = 2; i < values.Count; i+=2) { if (upperCase) { CurrentCursorPos.X = values[i]; CurrentCursorPos.Y = values[i + 1]; } else { CurrentCursorPos.X += values[i]; CurrentCursorPos.Y += values[i + 1]; } geomFile.CutTo3d(CurrentCursorPos); //Lineto(CurrentCursorPos) //CurrentCursorPos = [values[i], values[i + 1]]; } break; case 'z' or 'Z': geomFile.CloseCurve3d(true); geomFile.AddEntity(pathName, pathName); closed = true; //initialPos = false; //Lineto(InitialCursorPos); break; case 'L' or 'l': for (int i = 0; i < values.Count; i+=2) { if (upperCase) { CurrentCursorPos.X = values[i]; CurrentCursorPos.Y = values[i + 1]; } else { CurrentCursorPos.X += values[i]; CurrentCursorPos.Y += values[i + 1]; } geomFile.CutTo3d(CurrentCursorPos); } break; case 'H' or 'h': for (int i = 0; i < values.Count; i++) { CurrentCursorPos.X = upperCase ? values[i] : CurrentCursorPos.X + values[i]; geomFile.CutTo3d(CurrentCursorPos); } break; case 'V' or 'v': for (int i = 0; i < values.Count; i++) { CurrentCursorPos.Y = upperCase ? values[i] : CurrentCursorPos.Y + values[i]; geomFile.CutTo3d(CurrentCursorPos); } break; case 'C' or 'c': double x1, y1, x2, y2, x3, y3, x4, y4; x1 = CurrentCursorPos.X; y1 = CurrentCursorPos.Y; for (int i = 0; i < values.Count; i += 6) { if (upperCase) { x2 = values[i]; y2 = values[i+1]; x3 = values[i+2]; y3 = values[i+3]; x4 = values[i+4]; y4 = values[i+5]; } else { x2 = CurrentCursorPos.X + values[i]; y2 = CurrentCursorPos.Y + values[i+1]; x3 = CurrentCursorPos.X + values[i+2]; y3 = CurrentCursorPos.Y + values[i+3]; x4 = CurrentCursorPos.X + values[i+4]; y4 = CurrentCursorPos.Y + values[i+5]; } for (float f = 0F; f < 1F; f += precision) { float a = float.Pow(1-f, 3); float b = 3*f*float.Pow(1-f, 2); float c = 3*float.Pow(f, 2)*(1-f); float d = float.Pow(f, 3); double nx = a * x1 + b * x2 + c * x3 + d * x4; double ny = a * y1 + b * y2 + c * y3 + d * y4; geomFile.CutTo3d(new TST3DPoint { X = nx, Y = ny, Z = 0 }); } geomFile.CutTo3d(new TST3DPoint { X = x4, Y = y4, Z = 0 }); CurrentCursorPos.X = x4; CurrentCursorPos.X = y4; } break; case 'S' or 's': break; case 'Q' or 'q': case 'T' or 't': case 'A' or 'a': default: break; } //PathFunctions(func, values); } if (!closed) { geomFile.CloseCurve3d(false); geomFile.AddEntity(pathName, pathName); } return true; } bool DrawRect(XmlNode element, ISTGeomFiler geomFile) { /* x = "<coordinate>" y = "<coordinate>" width = "<length>" height = "<length>" rx = "<length>" ry = "<length>" */ Console.Write("found rect: {0}\n", element.Name); double x = Attribute2Double(element, "x"); double y = Attribute2Double(element, "y"); double width = Attribute2Double(element, "width"); double height = Attribute2Double(element, "height"); double rx = Attribute2Double(element, "rx"); double ry = Attribute2Double(element, "ry"); Console.Write("{0} {1} {2} {3} {4} {5}\n", x, y, width, height, rx, ry); TST3DPoint CurrentCursorPos = new() { X = x, Y = y, Z = 0 }; geomFile.StartCurve3d( element.Name, CurrentCursorPos ); CurrentCursorPos.X = width; geomFile.CutTo3d(CurrentCursorPos); CurrentCursorPos.Y = height; geomFile.CutTo3d(CurrentCursorPos); CurrentCursorPos.X = x; geomFile.CutTo3d(CurrentCursorPos); geomFile.CloseCurve3d(true); geomFile.AddEntity(element.Name, element.Name); return true; } bool DrawCircle(XmlNode element, ISTGeomFiler geomFile) { /* cx = "<coordinate>" cy = "<coordinate>" r = "<length>" */ Console.Write("found circle: {0}\n", element.Name); double cx = Attribute2Double(element, "cx"); double cy = Attribute2Double(element, "cy"); double r = Attribute2Double(element, "r"); Console.Write("{0} {1} {2}\n", cx, cy, r); TST3DPoint CurrentCursorPos = new() { X = cx, Y = cy, Z = 0 }; bool started = false; for (float f = 0F; f < Math.PI*2; f += precision / 4) { CurrentCursorPos.X = cx + Math.Sin(f) * r; CurrentCursorPos.Y = cy + Math.Cos(f) * r; if (started) { geomFile.CutTo3d(CurrentCursorPos); } else { geomFile.StartCurve3d( element.Name, CurrentCursorPos ); started = true; } } geomFile.CloseCurve3d(true); geomFile.AddEntity(element.Name, element.Name); return true; } bool DrawEllipse(XmlNode element, ISTGeomFiler geomFile) { /* cx = "<coordinate>" cy = "<coordinate>" rx = "<length>" ry = "<length>" */ Console.Write("found ellipse: {0}\n", element.Name); double cx = Attribute2Double(element, "cx"); double cy = Attribute2Double(element, "cy"); double rx = Attribute2Double(element, "rx"); double ry = Attribute2Double(element, "ry"); Console.Write("{0} {1} {2} {3}\n", cx, cy, rx, ry); TST3DPoint CurrentCursorPos = new() { X = cx, Y = cy, Z = 0 }; bool started = false; for (float f = 0F; f < Math.PI*2; f += precision / 4) { CurrentCursorPos.X = cx + Math.Sin(f) * rx; CurrentCursorPos.Y = cy + Math.Cos(f) * ry; if (started) { geomFile.CutTo3d(CurrentCursorPos); } else { geomFile.StartCurve3d( element.Name, CurrentCursorPos ); started = true; } } geomFile.CloseCurve3d(true); geomFile.AddEntity(element.Name, element.Name); return true; } bool DrawLine(XmlNode element, ISTGeomFiler geomFile) { /* x1 = "<coordinate>" y1 = "<coordinate>" x2 = "<coordinate>" y2 = "<coordinate>" */ Console.Write("found line: {0}\n", element.Name); double x1 = Attribute2Double(element, "x1"); double y1 = Attribute2Double(element, "y1"); double x2 = Attribute2Double(element, "x2"); double y2 = Attribute2Double(element, "y2"); Console.Write("{0} {1} {2} {3}\n", x1, y1, x2, y2); TST3DPoint CurrentCursorPos = new() { X = x1, Y = y1, Z = 0 }; geomFile.StartCurve3d( element.Name, CurrentCursorPos ); CurrentCursorPos.X = x2; CurrentCursorPos.Y = y2; geomFile.CutTo3d(CurrentCursorPos); geomFile.CloseCurve3d(false); geomFile.AddEntity(element.Name, element.Name); return true; } bool DrawPolyline(XmlNode element, ISTGeomFiler geomFile, bool closed) { /* points = "<list-of-points>" */ Console.Write("found polyline: {0}\n", element.Name); Console.Write("{0}\n", element.Attributes.GetNamedItem("points").Value); List<double> values = []; MatchCollection valueMatches = Regex.Matches(element.Attributes["points"].Value, @"-?\d*\.?\d+", rxOpt); foreach (Match valueMatch in valueMatches) { values.Add(double.Parse(valueMatch.Value, CultureInfo.InvariantCulture)); } if (values.Count % 2 != 0) throw new Exception("Error: points amount of polyline is not even!"); TST3DPoint CurrentCursorPos = new() { X = values[0], Y = values[1], Z = 0 }; geomFile.StartCurve3d( element.Name, CurrentCursorPos ); for (int i = 2; i < values.Count; i += 2) { CurrentCursorPos.X = values[i]; CurrentCursorPos.Y = values[i + 1]; geomFile.CutTo3d(CurrentCursorPos); } geomFile.CloseCurve3d(closed); geomFile.AddEntity(element.Name, element.Name); return true; } bool DrawPolygon(XmlElement element, ISTGeomFiler geomFile) { /* points = "<list-of-points>" */ Console.Write("found polygon: {0}\n", element.Name); Console.Write("{0}\n", element.GetAttribute("points")); return true; } bool GeometryProcessing(XmlNode element, ISTGeomFiler geomFile) => element.Name switch { "path" => DrawPath(element, geomFile), "rect" => DrawRect(element, geomFile), "circle" => DrawCircle(element, geomFile), "ellipse" => DrawEllipse(element, geomFile), "line" => DrawLine(element, geomFile), "polyline" => DrawPolyline(element, geomFile, false), "polygon" => DrawPolyline(element, geomFile, true), _ => false }; /* void TraverseXMLElements(XmlElement element, ISTGeomFiler geomFile) { Console.Write("Traversing {0}\n", element.Name); foreach (XmlElement childEl in element.ChildNodes) { //Console.Write("{0}\n", childEl.Name); //if (childEl.HasChildNodes)//.IsEmpty) //{ //if (!GeometryProcessing(childEl, geomFile)) //{ Console.Write("GeometryProcessing {0}\n", childEl.Name); GeometryProcessing(childEl, geomFile); if (childEl.HasChildNodes)//.IsEmpty) { //Console.Write("{0}\n", childEl.Name); TraverseXMLElements(childEl, geomFile); } //} } } */ void TraverseXMLNodes(XmlNode node, ISTGeomFiler geomFile) { Console.Write("Traversing node: {0}\n", node.Name); foreach (XmlNode childNode in node.ChildNodes) { //Console.Write("GeometryProcessing {0}\n", childNode.Name); if (childNode.HasChildNodes) { TraverseXMLNodes(childNode, geomFile); } else { GeometryProcessing(childNode, geomFile); //Console.Write("node have no childs: {0}\n", childNode.Name); } } } /// <inheritdoc /> public void Run(IExtensionUtilityContext context, out TResultStatus resultStatus) { resultStatus = default; FileStream fs = new(@"D:\projects\cam-api-examples\Geometry\ExtensionUtilityGeomCustomImportNet\output.txt", FileMode.OpenOrCreate, FileAccess.Write); StreamWriter writer = new(fs); Console.SetOut(writer); string filePath = @"D:\\1projects\\data\\cubic02.svg"; //@"C:\Users\zu\cubic02.svg"; //@"D:\projects\1data\Floral-Design-Elements.svg"; Console.Write("Opening a file: {0}\n", filePath); try { XmlDocument content = new(); content.Load(filePath); //XmlElement rootEl = content.DocumentElement ?? throw new Exception("File contain no elements: {0}\n"); //XmlAttributeCollection attributes = rootEl.Attributes; var sgfFilePath = Path.Combine(Directory.GetCurrentDirectory(), "example_svg.sgf"); // create object to write geometry using var wrapperFactoryGeomFiler = SystemExtensionFactory.GetSingletonExtension<ICamApiFactoryGeometryFile>("Extension.Global.Singletons.GeomFile"); var factoryGeomFiler = wrapperFactoryGeomFiler.Instance ?? throw new Exception("Can't get geometry filer singleton"); using var wrapperGeomFile = new ComWrapper<ISTGeomFiler>(factoryGeomFiler.CreateObject()); ISTGeomFiler? geomFile = wrapperGeomFile.Instance ?? throw new Exception("Can't create geometry filer object"); // beginning of the file if (!geomFile.StartFile(sgfFilePath)) throw new Exception("Can't start file: " + sgfFilePath); geomFile.StartModel(); try { try { // set point, we are going to use it as a coordinate system geomFile.SetCurrentTransform(T3DMatrix.Unit.vT, T3DMatrix.Unit.vZ, T3DMatrix.Unit.vX); // item in geometry objects tree geomFile.StartGroupEntity("curves"); try { //Console.Write("TraverseXMLElements {0}\n", rootEl.Name); foreach (XmlNode child in content.ChildNodes) { TraverseXMLNodes(child, geomFile); } } finally { geomFile.CloseGroupEntity(); } } finally { geomFile.CloseModel(); } } finally { geomFile.CloseFile(); writer.Close(); fs.Close(); } ImportHelper.Import(context, sgfFilePath); } catch (Exception e) { resultStatus.Code = TResultStatusCode.rsError; resultStatus.Description = e.Message; } } }