public void testMultiShape(SpatialContext ctx) { this.ctx = ctx; if (ctx.IsGeo()) { return; //TODO not yet supported! } //come up with some random shapes int NUM_SHAPES = random.Next(1, 5); var shapes = new List <Rectangle>(NUM_SHAPES); while (shapes.Count < NUM_SHAPES) { shapes.Add(RandomRectangle(20)); } var multiShape = new MultiShape(shapes.Cast <Shape>(), ctx); //test multiShape.getBoundingBox(); Rectangle msBbox = multiShape.GetBoundingBox(); if (shapes.Count == 1) { Assert.Equal(shapes[0], msBbox.GetBoundingBox()); } else { foreach (Rectangle shape in shapes) { assertRelation("bbox contains shape", SpatialRelation.CONTAINS, msBbox, shape); } } //TODO test multiShape.relate() }
public ViewGeometry(List <ViewGeometry> collisionMap, ICollisionManager collisionManager) { Shapes = new List <Shape>(); ElementRelativePositioning = new Dictionary <string, Point>(); CollisionMap = collisionMap; Geometry = new MultiShape(new Vertex[0]); CollisionManager = collisionManager; PreviousCollisions = new List <ViewGeometry>(); CurrentCollisions = new List <ViewGeometry>(); }
//----------------------------------------------------------------------- // //ORIGINAL LINE: MultiShape _booleanOperation(const Shape& STLAllocator<U, AllocPolicy>, BooleanOperationType opType) const private MultiShape _booleanOperation(Shape other, BooleanOperationType opType) { if (!mClosed || mPoints.size() < 2) OGRE_EXCEPT("Ogre::Exception::ERR_INVALID_STATE", "Current shapes must be closed and has to contain at least 2 points!", "Procedural::Shape::_booleanOperation(const Procedural::Shape&, Procedural::BooleanOperationType)"); if (!other.mClosed || other.mPoints.size() < 2) OGRE_EXCEPT("Ogre::Exception::ERR_INVALIDPARAMS", "Other shapes must be closed and has to contain at least 2 points!", "Procedural::Shape::_booleanOperation(const Procedural::Shape&, Procedural::BooleanOperationType)"); ; // Compute the intersection between the 2 shapes std_vector<IntersectionInShape> intersections = new std_vector<IntersectionInShape>(); _findAllIntersections(other, ref intersections); // Build the resulting shape if (intersections.empty()) { if (isPointInside(other.getPoint(0))) { // Shape B is completely inside shape A if (opType == BooleanOperationType.BOT_UNION) { MultiShape ms = new MultiShape(); ms.addShape(this); return ms; } else if (opType == BooleanOperationType.BOT_INTERSECTION) { MultiShape ms = new MultiShape(); ms.addShape(other); return ms; } else if (opType == BooleanOperationType.BOT_DIFFERENCE) { MultiShape ms = new MultiShape(); ms.addShape(this); ms.addShape(other); ms.getShape(1).switchSide(); return ms; } } else if (other.isPointInside(getPoint(0))) { // Shape A is completely inside shape B if (opType == BooleanOperationType.BOT_UNION) { MultiShape ms = new MultiShape(); ms.addShape(other); return ms; } else if (opType == BooleanOperationType.BOT_INTERSECTION) { MultiShape ms = new MultiShape(); ms.addShape(this); return ms; } else if (opType == BooleanOperationType.BOT_DIFFERENCE) { MultiShape ms = new MultiShape(); ms.addShape(this); ms.addShape(other); ms.getShape(0).switchSide(); return ms; } } else { if (opType == BooleanOperationType.BOT_UNION) { MultiShape ms = new MultiShape(); ms.addShape(this); ms.addShape(other); return ms; } else if (opType == BooleanOperationType.BOT_INTERSECTION) return new MultiShape(); //empty result else if (opType == BooleanOperationType.BOT_DIFFERENCE) return new MultiShape(); //empty result } } MultiShape outputMultiShape = new MultiShape(); Shape[] inputShapes = new Shape[2]; inputShapes[0] = this; inputShapes[1] = other; while (!intersections.empty()) { Shape outputShape = new Shape(); byte shapeSelector = 0; // 0 : first shape, 1 : second shape Vector2 currentPosition = intersections[0].position;//intersections.GetEnumerator().position; IntersectionInShape firstIntersection = intersections[0];//*intersections.GetEnumerator(); uint currentSegment = firstIntersection.index[shapeSelector]; //C++ TO C# CONVERTER TODO TASK: There is no direct equivalent to the STL vector 'erase' method in C#: //intersections.erase(intersections.GetEnumerator());//ÒƳý intersections.erase(firstIntersection, true); outputShape.addPoint(currentPosition); sbyte isIncreasing = 0; // +1 if increasing, -1 if decreasing, 0 if undefined if (!_findWhereToGo(inputShapes, opType, firstIntersection, ref shapeSelector, ref isIncreasing, ref currentSegment)) { // That intersection is located on a place where the resulting shape won't go => discard continue; } while (true) { // find the closest intersection on the same segment, in the correct direction //List<IntersectionInShape>.Enumerator found_next_intersection = intersections.end(); IntersectionInShape found_next_intersection = intersections[intersections.Count - 1]; int found_next_intersection_pos = -1; float distanceToNextIntersection = float.MaxValue;// std.numeric_limits<Real>.max(); uint nextPoint = currentSegment + (uint)(isIncreasing == 1 ? 1 : 0); bool nextPointIsOnIntersection = false; //for (List<IntersectionInShape>.Enumerator it = intersections.GetEnumerator(); it.MoveNext(); ++it) for (int i = 0; i < intersections.Count; i++) { IntersectionInShape it = intersections[i]; if (currentSegment == it.index[shapeSelector]) { if (((it.position - currentPosition).DotProduct(it.position - inputShapes[shapeSelector].getPoint((int)nextPoint)) < 0f) || (it.onVertex[shapeSelector] && nextPoint == it.index[shapeSelector])) { // found an intersection between the current one and the next segment point float d = (it.position - currentPosition).Length; if (d < distanceToNextIntersection) { // check if we have the nearest intersection found_next_intersection = it; found_next_intersection_pos = i; distanceToNextIntersection = d; } } } if (nextPoint == it.index[shapeSelector] && it.onVertex[shapeSelector]) nextPointIsOnIntersection = true; } // stop condition if (currentSegment == firstIntersection.index[shapeSelector]) { // we found ourselves on the same segment as the first intersection and no other if ((firstIntersection.position - currentPosition).DotProduct(firstIntersection.position - inputShapes[shapeSelector].getPoint((int)nextPoint)) < 0f) { float d = (firstIntersection.position - currentPosition).Length; if (d > 0.0f && d < distanceToNextIntersection) { outputShape.close(); break; } } } // We actually found the next intersection => change direction and add current intersection to the list //if (found_next_intersection.MoveNext()) //if (intersections.Count > 1) { if (found_next_intersection_pos != -1) { //IntersectionInShape currentIntersection = found_next_intersection.Current; IntersectionInShape currentIntersection = found_next_intersection; intersections.erase(found_next_intersection, true); //IntersectionInShape currentIntersection = intersections[intersections.Count - 1]; outputShape.addPoint(currentIntersection.position); bool result = _findWhereToGo(inputShapes, opType, currentIntersection, ref shapeSelector, ref isIncreasing, ref currentSegment); if (result == null) { OGRE_EXCEPT("Ogre::Exception::ERR_INTERNAL_ERROR", "We should not be here!", "Procedural::Shape::_booleanOperation(const Procedural::Shape&, Procedural::BooleanOperationType)"); ; } } else { // no intersection found for the moment => just continue on the current segment if (!nextPointIsOnIntersection) { if (isIncreasing == 1) currentPosition = inputShapes[shapeSelector].getPoint((int)currentSegment + 1); else currentPosition = inputShapes[shapeSelector].getPoint((int)currentSegment); outputShape.addPoint(currentPosition); } currentSegment = (uint)Utils.modulo((int)currentSegment + isIncreasing, inputShapes[shapeSelector].getSegCount()); } } outputMultiShape.addShape(outputShape); } return outputMultiShape; }
// * // * Applies a "thickness" to a shape, ie a bit like the extruder, but in 2D // * <table border="0" width="100%"><tr><td>\image html shape_thick1.png "Start shape (before thicken)"</td><td>\image html shape_thick2.png "Result (after thicken)"</td></tr></table> // //----------------------------------------------------------------------- public MultiShape thicken(float amount) { if (!mClosed) { Shape s = new Shape(); s.setOutSide(mOutSide); for (int i = 0; i < mPoints.Count; i++) s.addPoint(mPoints[i] + amount * getAvgNormal((uint)i)); for (int i = mPoints.Count - 1; i >= 0; i--) s.addPoint(mPoints[i] - amount * getAvgNormal((uint)i)); s.close(); return new MultiShape().addShape(s); } else { MultiShape ms = new MultiShape(); Shape s1 = new Shape(); for (int i = 0; i < mPoints.Count; i++) s1.addPoint(mPoints[i] + amount * getAvgNormal((uint)i)); s1.close(); s1.setOutSide(mOutSide); ms.addShape(s1); Shape s2 = new Shape(); for (int i = 0; i < mPoints.Count; i++) s2.addPoint(mPoints[i] - amount * getAvgNormal((uint)i)); s2.close(); s2.setOutSide(mOutSide == Side.SIDE_LEFT ? Side.SIDE_RIGHT : Side.SIDE_LEFT); ms.addShape(s2); return ms; } }
//----------------------------------------------------------------------- void parsePath(ref MultiShape @out, XmlNode pPathNode) { //if (pPathNode->first_attribute("d")) if (pPathNode.Attributes["d"] != null) { string temp = xtrim(pPathNode.Attributes["d"].Value, " .-0123456789mMlLhHvVcCsSqQtTaAzZ"); std_vector<string> parts = split(temp, " "); for (int i = 0; i < parts.size(); i++) if (parts[i].Length > 1 && !(parts[i][0] == '-' || ('0' <= parts[i][0] && parts[i][0] <= '9'))) { parts.insert(parts.begin() + i + 1, parts[i] + 1); //parts[i].erase(1, parts[i].size()); parts[i] = parts[i].Remove(1); } SvgLoaderPath sp = new SvgLoaderPath(parts, mNumSeg); if (!sp.isValid()) return; Shape ss = sp.getSvgShape(); Vector2 line = ss.getPoint(1) - ss.getPoint(0); //Real deg = line.angleBetween(ss.getPoint(2) - ss.getPoint(0)).valueDegrees(); float deg = Utils.angleBetween(line, ss.getPoint(2) - ss.getPoint(0)).ValueDegrees; if ((0 <= deg && deg <= 180.0f) || (-180.0f <= deg && deg <= 0)) ss.setOutSide(Side.SIDE_LEFT); else ss.setOutSide(Side.SIDE_RIGHT); //if(pPathNode->first_attribute("id")) // ss.id = pPathNode->first_attribute("id")->value(); ss.translate(getAttribTranslate(pPathNode)); @out.addShape(ss); } }
//----------------------------------------------------------------------- void parsePolygon(ref MultiShape @out, XmlNode pPolygonNode) { //if (pPolygonNode->first_attribute("points")) if (pPolygonNode.Attributes["points"] != null) { //if (pPolygonNode->first_attribute("points")->value_size() < 3) return; if (string.IsNullOrEmpty(pPolygonNode.Attributes["points"].Value)) return; if (pPolygonNode.Attributes["points"].Value.Length < 3) return; string temp = xtrim(pPolygonNode.Attributes["points"].Value); std_vector<string> pts = split(temp, " "); if (pts.size() == 0) return; Shape s = new Shape(); for (int i = 0; i < pts.size() - 1; i += 2) s.addPoint(parseReal(pts[i + 0]), parseReal(pts[i + 1])); if (s.getPointsReference().size() == 0) return; s.close(); // if(pPolygonNode->first_attribute("id")) // ss.id = pPolygonNode->first_attribute("id")->value(); s.translate(getAttribTranslate(pPolygonNode)); @out.addShape(s); } }
//----------------------------------------------------------------------- void parseEllipse(ref MultiShape @out, XmlNode pEllipseNode) { float rx = getAttribReal(pEllipseNode, "rx"); float ry = getAttribReal(pEllipseNode, "ry"); if (rx <= 0.0f || ry <= 0.0f) return; Shape s = new EllipseShape().setNumSeg(mNumSeg).setRadiusX(rx).setRadiusY(ry).realizeShape(); // if(pEllipseNode->first_attribute("id")) // ss.id = pEllipseNode->first_attribute("id")->value(); float position_x = getAttribReal(pEllipseNode, "cx"); float position_y = getAttribReal(pEllipseNode, "cy"); Vector2 position = new Vector2(position_x, position_y); Vector2 trans = getAttribTranslate(pEllipseNode); position += trans; s.translate(position); @out.addShape(s); }
//----------------------------------------------------------------------- void parseCircle(ref MultiShape @out, XmlNode pCircleNode) { float r = getAttribReal(pCircleNode, "r"); if (r <= 0.0f) return; Shape s = new CircleShape().setNumSeg(mNumSeg).setRadius(r).realizeShape(); // if(pCircleNode->first_attribute("id")) // ss.id = pCircleNode->first_attribute("id")->value(); float position_x = getAttribReal(pCircleNode, "cx"); float position_y = getAttribReal(pCircleNode, "cy"); Vector2 position = new Vector2(position_x, position_y); Vector2 trans = getAttribTranslate(pCircleNode); position += trans; s.translate(position); @out.addShape(s); }
//----------------------------------------------------------------------- void parseRect(ref MultiShape @out, XmlNode pRectNode) { float width = getAttribReal(pRectNode, "width"); float height = getAttribReal(pRectNode, "height"); if (width <= 0.0f || height <= 0.0f) return; Shape s = new RectangleShape().setHeight(height).setWidth(width).realizeShape(); // if(pRectNode->first_attribute("id")) // ss.id = pRectNode->first_attribute("id")->value(); //Vector2 position; float position_x = getAttribReal(pRectNode, "x"); float position_y = getAttribReal(pRectNode, "y"); Vector2 position = new Vector2(position_x, position_y); // Our rectangle are centered, but svg rectangles are defined by their corners position += 0.5f * new Vector2(width, height); Vector2 trans = getAttribTranslate(pRectNode); position += trans; s.translate(position); @out.addShape(s); }
//----------------------------------------------------------------------- //private: void parseChildNode(ref MultiShape @out, XmlNode pChild) { string name = pChild.Name; if (name.Length > 3) { //if (stricmp(name.c_str(), "rect") == 0) if (stricmp(name, "rect") == 0) parseRect(ref @out, pChild); else if (stricmp(name, "circle") == 0) parseCircle(ref @out, pChild); else if (stricmp(name, "ellipse") == 0) parseEllipse(ref @out, pChild); else if (stricmp(name, "polygon") == 0 || stricmp(name, "polyline") == 0) parsePolygon(ref @out, pChild); else if (stricmp(name, "path") == 0) parsePath(ref @out, pChild); // svg path is a shape } //rapidxml::xml_node<>* pSubChildNode = pChild->first_node(); XmlNode pSubChildNode = pChild.FirstChild; while (pSubChildNode != null) { parseChildNode(ref @out, pSubChildNode); pSubChildNode = pSubChildNode.NextSibling; } }
public void parseSvgFile(ref MultiShape @out, string fileName, string groupName, int segmentsNumber) { mNumSeg = (uint)segmentsNumber; //rapidxml::xml_document<> XMLDoc; // character type defaults to char XmlDocument XMLDoc = new XmlDocument(); // DataStreamPtr stream = ResourceGroupManager::getSingleton().openResource(fileName, groupName); //char* svg = strdup(stream->getAsString().c_str()); //XMLDoc.parse<0>(svg); //rapidxml::xml_node<>* pXmlRoot = XMLDoc.first_node("svg"); XmlNode pXmlRoot = XMLDoc.SelectSingleNode("svg"); //if (pXmlRoot == NULL) return; if (pXmlRoot == null) return; //rapidxml::xml_node<>* pXmlChildNode = pXmlRoot->first_node(); XmlNode pXmlChildNode = pXmlRoot.FirstChild; while (pXmlChildNode != null) { parseChildNode(ref @out, pXmlChildNode); //pXmlChildNode = pXmlChildNode->next_sibling(); pXmlChildNode = pXmlChildNode.NextSibling; } }
//public: /** Parses a SVG file @param out MultiShape object where to store shapes from svg file @param fileName Filename of svg file @param groupName Resource group where svg file is listed @param segmentsNumber Number of segments for curves */ //void parseSvgFile(MultiShape& out, const Ogre::String& fileName, const Ogre::String& groupName = Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, int segmentsNumber = 8); public void parseSvgFile(ref MultiShape ms, string fileName) { parseSvgFile(ref ms, fileName, Mogre.ResourceGroupManager.DEFAULT_RESOURCE_GROUP_NAME, 8); }
// * // * Build a MultiShape from chars (one Shape per character) // * \exception Ogre::InternalErrorException Freetype error // * \todo Need to split shapes of multi region chars. For example the letter \c O // * has two shapes, but they are connected to one shape. // public MultiShape realizeShapes() { MultiShape retVal = new MultiShape(); FT_Library ftlib = new FT_Library(); FT_Face face = new FT_Face(); FT_GlyphSlot slot = new FT_GlyphSlot(); FT_Error error = FT_Init_FreeType(ftlib); if (error == 0) { error = FT_New_Face(ftlib, getFontFileByName().c_str(), 0, face); if (error == FT_Err_Unknown_File_Format) { //C++ TO C# CONVERTER TODO TASK: There is no direct equivalent in C# to the C++ __LINE__ macro: //C++ TO C# CONVERTER TODO TASK: There is no direct equivalent in C# to the C++ __FILE__ macro: throw ExceptionFactory.create(Mogre.ExceptionCodeType<Mogre.Exception.ExceptionCodes.ERR_INTERNAL_ERROR>(), "FreeType ERROR: FT_Err_Unknown_File_Format", "Procedural::TextShape::realizeShapes()", __FILE__, __LINE__); ; } else if (error != null) { //C++ TO C# CONVERTER TODO TASK: There is no direct equivalent in C# to the C++ __LINE__ macro: //C++ TO C# CONVERTER TODO TASK: There is no direct equivalent in C# to the C++ __FILE__ macro: throw ExceptionFactory.create(Mogre.ExceptionCodeType<Mogre.Exception.ExceptionCodes.ERR_INTERNAL_ERROR>(), "FreeType ERROR: FT_New_Face - " + StringConverter.toString(error), "Procedural::TextShape::realizeShapes()", __FILE__, __LINE__); ; } else { FT_Set_Pixel_Sizes(face, 0, mFontSize); int px = 0; int py = 0; slot = face.glyph; for (int n = 0; n < mText.length(); n++) { error = FT_Load_Char(face, mText[n], FT_LOAD_NO_BITMAP); if (error != null) continue; Shape s = new Shape(); int nContours = face.glyph.outline.n_contours; int startPos = 0; string tags = face.glyph.outline.tags; FT_Vector[] vec = face.glyph.outline.points; for (int k = 0; k < nContours; k++) { if (k > 0) startPos = face.glyph.outline.contours[k-1]+1; int endPos = face.glyph.outline.contours[k]+1; Vector2 lastPoint = Vector2.ZERO; for (int j = startPos; j < endPos; j++) { if (FT_CURVE_TAG(tags[j]) == FT_CURVE_TAG_ON) { lastPoint = Vector2((float)vec[j].x, (float)vec[j].y); s.addPoint(lastPoint / 64.0f); } else { if (FT_CURVE_TAG(tags[j]) == FT_CURVE_TAG_CUBIC) { int prevPoint = j - 1; if (j == 0) prevPoint = endPos - 1; int nextIndex = j + 1; if (nextIndex >= endPos) nextIndex = startPos; Vector2[] nextPoint = new Vector2[nextIndex]((float)vec.x, (float)vec[nextIndex].y); if ((FT_CURVE_TAG(tags[prevPoint]) != FT_CURVE_TAG_ON) && (FT_CURVE_TAG(tags[prevPoint]) == FT_CURVE_TAG_CUBIC)) { BezierCurve2 bc = new BezierCurve2(); bc.addPoint(Vector2((float)vec[prevPoint].x, (float)vec[prevPoint].y) / 64.0f); bc.addPoint(Vector2((float)vec[j].x, (float)vec[j].y) / 64.0f); bc.addPoint(Vector2((float)vec[nextIndex].x, (float)vec[nextIndex].y) / 64.0f); s.appendShape(bc.realizeShape()); } } else { Vector2[] conicPoint = new Vector2[j]((float)vec.x, (float)vec[j].y); if (j == startPos) { if ((FT_CURVE_TAG(tags[endPos-1]) != FT_CURVE_TAG_ON) && (FT_CURVE_TAG(tags[endPos-1]) != FT_CURVE_TAG_CUBIC)) { Vector2[] lastConnic = new Vector2[endPos - 1]((float)vec.x, (float)vec[endPos - 1].y); lastPoint = (conicPoint + lastConnic) / 2; } } int nextIndex = j + 1; if (nextIndex >= endPos) nextIndex = startPos; Vector2[] nextPoint = new Vector2[nextIndex]((float)vec.x, (float)vec[nextIndex].y); bool nextIsConnic = (FT_CURVE_TAG(tags[nextIndex]) != FT_CURVE_TAG_ON) && (FT_CURVE_TAG(tags[nextIndex]) != FT_CURVE_TAG_CUBIC); if (nextIsConnic) nextPoint = (conicPoint + nextPoint) / 2; int pc = s.getPointCount(); BezierCurve2 bc = new BezierCurve2(); if (pc == 0) bc.addPoint(Vector2.ZERO); else bc.addPoint(s.getPoint(pc - 1)); bc.addPoint(lastPoint / 64.0f); bc.addPoint(conicPoint / 64.0f); bc.addPoint(nextPoint / 64.0f); if (pc == 0) s.appendShape(bc.realizeShape()); else { List<Vector2> subShape = bc.realizeShape().getPoints(); for (List<Vector2>.Enumerator iter = subShape.GetEnumerator(); iter.MoveNext(); iter++) { if (iter != subShape.GetEnumerator()) s.addPoint(iter.Current); } } if (nextIsConnic) // //ORIGINAL LINE: lastPoint = nextPoint; lastPoint=(nextPoint); } } } } s.close(); s.translate((float)px, (float)py); retVal.addShape(s); px += slot.advance.x >> 6; py += slot.advance.y >> 6; } FT_Done_Face(face); } FT_Done_FreeType(ftlib); } else { //C++ TO C# CONVERTER TODO TASK: There is no direct equivalent in C# to the C++ __LINE__ macro: //C++ TO C# CONVERTER TODO TASK: There is no direct equivalent in C# to the C++ __FILE__ macro: throw ExceptionFactory.create(Mogre.ExceptionCodeType<Mogre.Exception.ExceptionCodes.ERR_INTERNAL_ERROR>(), "FreeType ERROR: FT_Init_FreeTyp", "Procedural::TextShape::realizeShapes()", __FILE__, __LINE__); ; } return retVal; }