@SuppressWarnings("unchecked") private void adjustAnchors(EditPart editPart) { if (editPart instanceof IGraphicalEditPart) { View view = ((IGraphicalEditPart) editPart).getNotationView(); EList<Edge> targetEdges = view.getTargetEdges(); for (Edge edge : targetEdges) { Anchor targetAnchor = edge.getTargetAnchor(); if (targetAnchor instanceof IdentityAnchor) { PrecisionPoint anchorPoint = BaseSlidableAnchor.parseTerminalString(((IdentityAnchor) targetAnchor) .getId()); IFigure figure = ((IGraphicalEditPart) editPart).getFigure(); Dimension sizeBefore = figure.getBounds().getSize(); float widthFactor = (float) (sizeBefore.width() + request.getSizeDelta().width()) / (float) sizeBefore.width(); float heightFactor = (float) (sizeBefore.height() + request.getSizeDelta().height()) / (float) sizeBefore.height(); PrecisionPoint newPoint = new PrecisionPoint(anchorPoint.preciseX() / widthFactor, anchorPoint.preciseY() / heightFactor); ((IdentityAnchor) targetAnchor).setId(composeTerminalString(newPoint)); } } } }
/** * This method can be overridden by clients to customize the snapping behavior. * * @param request * the <code>ChangeBoundsRequest</code> from which the move delta can be extracted and updated * @since 3.4 */ protected void snapPoint(ChangeBoundsRequest request) { Point moveDelta = request.getMoveDelta(); if (editpart != null && getOperationSet().size() > 0) snapToHelper = (SnapToHelper) editpart.getParent().getAdapter(SnapToHelper.class); if (snapToHelper != null && !getCurrentInput().isModKeyDown(MODIFIER_NO_SNAPPING)) { PrecisionRectangle baseRect = sourceRectangle.getPreciseCopy(); PrecisionRectangle jointRect = compoundSrcRect.getPreciseCopy(); baseRect.translate(moveDelta); jointRect.translate(moveDelta); PrecisionPoint preciseDelta = new PrecisionPoint(moveDelta); snapToHelper.snapPoint(request, PositionConstants.HORIZONTAL | PositionConstants.VERTICAL, new PrecisionRectangle[] { baseRect, jointRect }, preciseDelta); request.setMoveDelta(preciseDelta); } }
public void setNormalizedPointList(PrecisionPointList norms) { this.norms = norms; // dimensions Dimension parentSize = getParent().getSize(); int parentY = (int) (parentSize.height * ChartFigure.DESIGN_BOUNDARY_PERCENTAGE); int parentHeight = (int) (parentSize.height - 2*parentSize.height*ChartFigure.DESIGN_BOUNDARY_PERCENTAGE); PointList pointList = new PointList(); int length = norms.size(); int previous_x = Integer.MIN_VALUE; int previous_y = Integer.MIN_VALUE; for (int i=0; i<length; i++) { PrecisionPoint pt = (PrecisionPoint) norms.getPoint(i); int x = Math.max(0, (int)pt.preciseX()); int y = (int)((parentHeight * (1.0 - pt.preciseY())) + parentY); if ((x != previous_x) || (y != previous_y)) { pointList.addPoint(x, y); previous_x = x; previous_y = y; } } setPoints(pointList); intArray = pointList.toIntArray(); }
@Override public Point getPoint(Point p, int index) { if (index < 0 || index >= size) throw new IndexOutOfBoundsException( "Index: " + index + //$NON-NLS-1$ ", Size: " + size); //$NON-NLS-1$ index *= 2; if (p instanceof PrecisionPoint) { PrecisionPoint preciseP = (PrecisionPoint) p; preciseP.setPreciseX(points[index]); preciseP.setPreciseY(points[index + 1]); // preciseP.updateInts(); done automatically from setPreciseX/Y } else { p.x = (int)Math.floor(points[index] + 0.000000001); p.y = (int)Math.floor(points[index + 1] + 0.000000001); } return p; }
@Override public void insertPoint(Point p, int index) { if (bounds != null && !bounds.contains(p)) bounds = null; if (index > size || index < 0) throw new IndexOutOfBoundsException( "Index: " + index + //$NON-NLS-1$ ", Size: " + size); //$NON-NLS-1$ index *= 2; int length = points.length; double old[] = points; points = new double[length + 2]; System.arraycopy(old, 0, points, 0, index); System.arraycopy(old, index, points, index + 2, length - index); if (p instanceof PrecisionPoint) { PrecisionPoint precisionPt = (PrecisionPoint)p; points[index] = precisionPt.preciseX(); points[index + 1] = precisionPt.preciseY(); } else { points[index] = p.x; points[index + 1] = p.y; } size++; }
@Override public void setPoint(Point pt, int index) { if (index < 0 || index >= size) throw new IndexOutOfBoundsException( "Index: " + index + //$NON-NLS-1$ ", Size: " + size); //$NON-NLS-1$ if (bounds != null && !bounds.contains(pt)) bounds = null; if (pt instanceof PrecisionPoint) { PrecisionPoint precisionPt = (PrecisionPoint)pt; points[index * 2] = precisionPt.preciseX(); points[index * 2 + 1] = precisionPt.preciseY(); } else { points[index * 2] = pt.x; points[index * 2 + 1] = pt.y; } }
/** * If auto scroll (also called auto expose) is being performed, the start * location moves during the scroll. This method updates that location. */ protected void repairStartLocation() { if (sourceRelativeStartPoint == null) return; IFigure figure = ((GraphicalEditPart) getSourceEditPart()).getFigure(); PrecisionPoint newStart = (PrecisionPoint) sourceRelativeStartPoint .getCopy(); figure.translateToAbsolute(newStart); Point delta = new Point(newStart.x - getStartLocation().x, newStart.y - getStartLocation().y); setStartLocation(newStart); // sourceRectangle and compoundSrcRect need to be updated as well when // auto-scrolling if (sourceRectangle != null) sourceRectangle.translate(delta); if (compoundSrcRect != null) compoundSrcRect.translate(delta); }
/** * This method can be overridden by clients to customize the snapping * behavior. * * @param request * the <code>ChangeBoundsRequest</code> from which the move delta * can be extracted and updated * @since 3.4 */ protected void snapPoint(ChangeBoundsRequest request) { Point moveDelta = request.getMoveDelta(); if (snapToHelper != null && request.isSnapToEnabled()) { PrecisionRectangle baseRect = sourceRectangle.getPreciseCopy(); PrecisionRectangle jointRect = compoundSrcRect.getPreciseCopy(); baseRect.translate(moveDelta); jointRect.translate(moveDelta); PrecisionPoint preciseDelta = new PrecisionPoint(moveDelta); snapToHelper.snapPoint(request, PositionConstants.HORIZONTAL | PositionConstants.VERTICAL, new PrecisionRectangle[] { baseRect, jointRect }, preciseDelta); request.setMoveDelta(preciseDelta); } }
/** * Calculates and returns this Bendpoint's new location. * * @return This Bendpoint's new location * @since 2.0 */ public Point getLocation() { PrecisionPoint a1 = new PrecisionPoint(getConnection() .getSourceAnchor().getReferencePoint()); PrecisionPoint a2 = new PrecisionPoint(getConnection() .getTargetAnchor().getReferencePoint()); getConnection().translateToRelative(a1); getConnection().translateToRelative(a2); return new PrecisionPoint( (a1.preciseX() + d1.preciseWidth()) * (1.0 - weight) + weight * (a2.preciseX() + d2.preciseWidth()), (a1.preciseY() + d1.preciseHeight()) * (1.0 - weight) + weight * (a2.preciseY() + d2.preciseHeight())); }
/** * In this relations browser, we want the related items to be displayed in * concentric circles around the center of the browser window. */ @SuppressWarnings("unchecked") private void setAroundCenter() { final Map<IItemModel, GraphicalEditPart> lRegistry = viewer.getEditPartRegistry(); final org.eclipse.swt.graphics.Point lSize = getSize(); final PrecisionPoint lTranslate = new PrecisionPoint(lSize.x / 2 - (RelationsConstants.ITEM_WIDTH / 2), (lSize.y / 2) - RelationsConstants.ITEM_HEIGHT); moveFigure(lRegistry, model.getCenter(), new PrecisionPoint(0, 0), lTranslate); final List<ItemAdapter> lRelated = model.getRelatedItems(); int lNumber = lRelated.size(); int lCount = 0; int lOffset = 0; final ItemPositionCalculator lCalculator = new ItemPositionCalculator(RelationsConstants.ITEM_WIDTH, RelationsConstants.ITEM_HEIGHT, getRadius(++lCount), lNumber); while (lCalculator.hasMore()) { lOffset = setPositions(lRegistry, lCalculator.getPositions(), lOffset, lRelated, lTranslate); lNumber -= lCalculator.getCount(); lCalculator.recalculate(getRadius(++lCount), lNumber); } setPositions(lRegistry, lCalculator.getPositions(), lOffset, lRelated, lTranslate); oldSize = lSize; }
public LineController ( final SymbolController controller, final Line element, final ResourceManager manager ) { super ( controller, manager ); this.figure = new PolylineShape () { @Override public void addNotify () { super.addNotify (); start (); } @Override public void removeNotify () { stop (); super.removeNotify (); } }; final PointList points = new PointList (); for ( final Position pos : element.getPoints () ) { final Point p = new PrecisionPoint ( pos.getX (), pos.getY () ); points.addPoint ( p ); } setPoints ( points ); controller.addElement ( element, this ); applyCommon ( element ); }
/** * Set points as string * <p> * <code> * 1.5;2.5|1.5;2.5 * </code> * </p> * * @param points */ public void setPointsString ( final String pointsString ) { final PointList pointList = new PointList (); final String[] points = pointsString.split ( "\\|" ); for ( final String point : points ) { final String[] toks = point.split ( ";" ); final PrecisionPoint p = new PrecisionPoint ( Double.parseDouble ( toks[0] ), Double.parseDouble ( toks[1] ) ); pointList.addPoint ( p ); } setPoints ( pointList ); }
public PolygonController ( final SymbolController controller, final Polygon element, final ResourceManager manager ) { super ( controller, manager ); this.figure = new PolygonShape () { @Override public void addNotify () { super.addNotify (); start (); } @Override public void removeNotify () { stop (); super.removeNotify (); } }; final PointList points = new PointList (); for ( final Position pos : element.getPoints () ) { final Point p = new PrecisionPoint ( pos.getX (), pos.getY () ); points.addPoint ( p ); } setPoints ( points ); controller.addElement ( element, this ); applyCommon ( element ); }
protected String composeTerminalString(PrecisionPoint p) { StringBuffer s = new StringBuffer(24); s.append(TERMINAL_START_CHAR); // 1 char s.append(p.preciseX()); // 10 chars s.append(TERMINAL_DELIMITER_CHAR); // 1 char s.append(p.preciseY()); // 10 chars s.append(TERMINAL_END_CHAR); // 1 char return s.toString(); // 24 chars max (+1 for safety, i.e. for string // termination) }
public Point getLocation() { Rectangle r = getOwner().getBounds(); Point p = new PrecisionPoint(r.x + r.width * xOffset, r.y + r.height * yOffset); getOwner().translateToAbsolute(p); return p; }
/** * Test for setConnectionAnchors */ @Test public void setConnectionAnchorsTest() { Pair<String, Class<?>> A = new Pair<String, Class<?>>("ClassA", org.eclipse.uml2.uml.Class.class); Pair<String, Class<?>> B = new Pair<String, Class<?>>("ClassB", org.eclipse.uml2.uml.Class.class); List<Pair<String, Class<?>>> objects = Arrays.asList(A, B); List<Pair<Pair<String, Class<?>>, Pair<String, Class<?>>>> associations = Arrays .asList(new Pair<Pair<String, Class<?>>, Pair<String, Class<?>>>( A, B)); init(objects, associations); @SuppressWarnings("unchecked") List<EditPart> eps = getDiagramEditPart().getChildren(); ClassEditPart classAEp = (ClassEditPart) eps.get(0); ClassEditPart classBEp = (ClassEditPart) eps.get(1); @SuppressWarnings("unchecked") List<ConnectionEditPart> conns = classBEp.getSourceConnections(); ConnectionEditPart assoc = conns.get(0); DiagramElementsModifier.setConnectionAnchors(assoc, "(1, 0.5)", "(0, 0.5)"); ConnectionAnchor source = classAEp.getSourceConnectionAnchor(assoc); ConnectionAnchor target = classBEp.getTargetConnectionAnchor(assoc); Point sourceReferencePoint = ((SlidableAnchor) source) .getReferencePoint(); Point targetReferencePoint = ((SlidableAnchor) target) .getReferencePoint(); PrecisionPoint sourceAnchor = SlidableAnchor.getAnchorRelativeLocation( sourceReferencePoint, source.getOwner().getBounds()); PrecisionPoint targetAnchor = SlidableAnchor.getAnchorRelativeLocation( targetReferencePoint, target.getOwner().getBounds()); Assert.assertEquals(new PrecisionPoint(1, 0.5), sourceAnchor); Assert.assertEquals(new PrecisionPoint(0, 0.5), targetAnchor); }
@Override @SuppressWarnings("unchecked") public void updatePointList() throws ProfileUpdatedException { final LineFigure figure = (LineFigure)getFigure(); Profile profile = getModel().getProfile(); PrecisionPointList pointList = null; if (profile != null) { // SPF-9109 Removed boolean check of isNumeric value that is // returned from createPrecisionPoints(pointList, profile). We will // go ahead and plot all data from the pointList. pointList = createPrecisionPoints(profile); if (pointList.size() > 0) { PrecisionPoint first_pt = (PrecisionPoint) pointList.getFirstPoint(); pointList.insertPoint(new PrecisionPoint(0, first_pt.preciseY()), 0); PrecisionPoint last_pt = (PrecisionPoint) pointList.getLastPoint(); pointList.addPrecisionPoint(figure.getParent().getBounds().width, last_pt.preciseY()); } } else { pointList = new PrecisionPointList(); } final PrecisionPointList list; if(profile!=null && (INTERPOLATION.INSTANTANEOUS == profile.getInterpolation())) { // Instantaneous, no need to strip midpoints list = new PrecisionPointList(pointList); } else { list = stripRedundantMidpoints(pointList); } GEFUtils.runInDisplayThread(this, new Runnable() { @Override public void run() { figure.setNormalizedPointList(list); } }); }
@Override public void addPoint(Point p) { if (p instanceof PrecisionPoint) { PrecisionPoint precisionPt = (PrecisionPoint)p; addPrecisionPoint(precisionPt.preciseX(), precisionPt.preciseY()); } else { addPrecisionPoint(p.preciseX(), p.preciseY()); } }
@Override public Point getPoint(int index) { if (index < 0 || index >= size) throw new IndexOutOfBoundsException( "Index: " + index + //$NON-NLS-1$ ", Size: " + size); //$NON-NLS-1$ index *= 2; return new PrecisionPoint(points[index], points[index + 1]); }
@Override public Point removePoint(int index) { bounds = null; if (index < 0 || index >= size) throw new IndexOutOfBoundsException( "Index: " + index + //$NON-NLS-1$ ", Size: " + size); //$NON-NLS-1$ index *= 2; PrecisionPoint pt = new PrecisionPoint(points[index], points[index + 1]); if (index != size * 2 - 2) System.arraycopy(points, index + 2, points, index, size * 2 - index - 2); size--; return pt; }
@Override public Point getBorderPoint(Point reference) { // 得到owner矩形,转换为绝对坐标 Rectangle r = Rectangle.SINGLETON; r.setBounds(getOwner().getBounds()); getOwner().translateToAbsolute(r); // 根据角度,计算锚点相对于owner中心点的偏移 double dx = 0.0, dy = 0.0; double tan = Math.atan2(r.height, r.width); if(angle >= -tan && angle <= tan) { dx = r.width >> 1; dy = dx * Math.tan(angle); } else if(angle >= tan && angle <= Math.PI - tan) { dy = r.height >> 1; dx = dy / Math.tan(angle); } else if(angle <= -tan && angle >= tan - Math.PI) { dy = -(r.height >> 1); dx = dy / Math.tan(angle); } else { dx = -(r.width >> 1); dy = dx * Math.tan(angle); } // 得到长方形中心点,加上偏移,得到最终锚点坐标 PrecisionPoint pp = new PrecisionPoint(r.getCenter()); pp.translate((int)dx, (int)dy); return new Point(pp); }
@Override public Point getBorderPoint(Point reference) { // 得到owner矩形,转换为绝对坐标 Rectangle r = Rectangle.SINGLETON; r.setBounds(getOwner().getBounds()); getOwner().translateToAbsolute(r); // 根据角度,计算锚点相对于owner中心点的偏移 /*double dx = 0.0, dy = 0.0; double tan = Math.atan2(r.height, r.width); if(angle >= -tan && angle <= tan) { dx = r.width >> 1; dy = dx * Math.tan(angle); } else if(angle >= tan && angle <= Math.PI - tan) { dy = r.height >> 1; dx = dy / Math.tan(angle); } else if(angle <= -tan && angle >= tan - Math.PI) { dy = -(r.height >> 1); dx = dy / Math.tan(angle); } else { dx = -(r.width >> 1); dy = dx * Math.tan(angle); }*/ double dx = 0.0, dy = 0.0; // 得到长方形中心点,加上偏移,得到最终锚点坐标 PrecisionPoint pp = new PrecisionPoint(r.getCenter()); if(pp.x-reference.x>=0) dx=-r.preciseWidth()/2; else dx=r.preciseWidth()/2; pp.translate((int)dx, (int)dy); return new Point(pp); }
/** * For a given angle, rotate the point list. * * @param angle * @param points * @return */ protected PointList rotatePoints(double angle, PointList points) { Transform t = new Transform(); t.setRotation(angle); Point center = new Point(getPreferredSize().width / 2, getPreferredSize().height / 2); PointList newEdges = TransformationHelper.rotatePoints(angle, edges, center); if (offset) newEdges.translate(t.getTransformed(new PrecisionPoint(DEFAULT_WIDTH * (RESIZEFACTOR - 1) / 2, -DEFAULT_WIDTH * (RESIZEFACTOR - 1) / 2))); return newEdges; }
private int setPositions(final Map<IItemModel, GraphicalEditPart> inRegistry, final List<PrecisionPoint> inPositions, final int inOffset, final List<ItemAdapter> inRelated, final PrecisionPoint inTranslate) { int outOffset = inOffset; for (final PrecisionPoint lPoint : inPositions) { moveFigure(inRegistry, inRelated.get(outOffset), lPoint, inTranslate); ++outOffset; } return outOffset; }
private void moveFigure(final Map<IItemModel, GraphicalEditPart> inRegistry, final ItemAdapter inModel, final PrecisionPoint inFrom, final PrecisionPoint inTranslate) { final GraphicalEditPart lEditPart = inRegistry.get(inModel); if (lEditPart != null) { lEditPart.getFigure().setLocation(inFrom.getTranslated(inTranslate)); } }
/** * Re-center the edit parts after a resize of the pane. */ @SuppressWarnings("unchecked") private void recenter(final PrecisionPoint inTranslate) { final Map<IItemModel, GraphicalEditPart> lRegistry = viewer.getEditPartRegistry(); for (final GraphicalEditPart lEditPart : lRegistry.values()) { final Point lFrom = lEditPart.getFigure().getBounds().getLocation(); lEditPart.getFigure().setLocation(lFrom.getTranslated(inTranslate)); } }
@Test public void testDo() { ItemPositionCalculator lCalculator = new ItemPositionCalculator(WIDTH, HEIGHT, RADIUS, 2); assertEquals("count 1", 2, lCalculator.getCount()); assertEquals("offset 1", 1.57079, lCalculator.getOffset(), 0.001); assertEquals("delta 1", 0.33221, lCalculator.getDelta(), 0.001); assertFalse("has more 1", lCalculator.hasMore()); lCalculator = new ItemPositionCalculator(WIDTH, HEIGHT, RADIUS, 5); assertEquals("count 2", 5, lCalculator.getCount()); assertEquals("offset 2", 1.3216357, lCalculator.getOffset(), 0.001); assertEquals("delta 2", 0.3322140, lCalculator.getDelta(), 0.001); assertFalse("has more 2", lCalculator.hasMore()); lCalculator = new ItemPositionCalculator(WIDTH, HEIGHT, RADIUS, 25); assertEquals("count 3", 16, lCalculator.getCount()); assertEquals("offset 3", 0.40804704, lCalculator.getOffset(), 0.001); assertEquals("delta 3", 0.33221408, lCalculator.getDelta(), 0.001); assertTrue("has more 3", lCalculator.hasMore()); final PrecisionPoint[] lExpected = new PrecisionPoint[] { new PrecisionPoint(79, -183), new PrecisionPoint(134, -147), new PrecisionPoint(175, -95), new PrecisionPoint(197, -33), new PrecisionPoint(197, 33), new PrecisionPoint(175, 95), new PrecisionPoint(134, 147), new PrecisionPoint(79, 183), new PrecisionPoint(-79, 183), new PrecisionPoint(-134, 147), new PrecisionPoint(-175, 95), new PrecisionPoint(-197, 33), new PrecisionPoint(-197, -33), new PrecisionPoint(-175, -95), new PrecisionPoint(-134, -147), new PrecisionPoint(-79, -183) }; final List<PrecisionPoint> lPoints = lCalculator.getPositions(); assertEquals("length", lExpected.length, lPoints.size()); for (int i = 0; i < lExpected.length; i++) { final PrecisionPoint lPoint = lPoints.get(i); assertEquals(lExpected[i].preciseX(), lPoint.preciseX(), 1); assertEquals(lExpected[i].preciseY(), lPoint.preciseY(), 1); } }
public void setPosition ( final double x, final double y ) { this.rect.setLocation ( new PrecisionPoint ( x, y ) ); this.figure.getParent ().setConstraint ( this.figure, this.rect ); }
@Override protected ConnectionAnchor createAnchor(PrecisionPoint p) { return createDefaultAnchor(); }
public FixedConnectionAnchor(IFigure owner, PrecisionPoint offset) { this(owner, offset.preciseX(), offset.preciseY()); }
/** * Applies a snapping correction to the given result. Snapping can occur in * the four primary directions: NORTH, SOUTH, EAST, WEST, as defined on * {@link PositionConstants}. By default a Point is treated as an empty * Rectangle. Only NORTH and WEST should be used in general. But SOUTH and * EAST may also be used. Similarly, VERTICAL and HORIZONTAL may be used to * allow a point to snap to the "center" or "middle" as defined by the * concrete subclass. * <P> * The returned value should be a subset of the given snapDirections based * on what correction was applied to the result. e.g., if the <b>x</b> value * was adjusted, the returned value should not contain WEST, EAST, or * HORIZONTAL. * <P> * All coordinate information received and returned by this method should be * in absolute coordinates. * * @param request * a request or <code>null</code> * @param snapDirections * the directions in which snapping should occur. * @param where * the rectangle used to determine snapping * @param result * the result * @return the remaining snap locations */ public int snapPoint(Request request, int snapDirections, PrecisionPoint where, PrecisionPoint result) { PrecisionRectangle rect = new PrecisionRectangle(); PrecisionRectangle resultRect = new PrecisionRectangle(); rect.setPreciseX(where.preciseX()); rect.setPreciseY(where.preciseY()); snapDirections = snapRectangle(request, snapDirections, rect, resultRect); result.setPreciseX(result.preciseX() + resultRect.preciseX()); result.setPreciseY(result.preciseY() + resultRect.preciseY()); return snapDirections; }
/** * A convenience method for snapping a Point based on an array of * rectangles. By default, this method will construct an empty rectangle at * the same locations as the provided point, and call * {@link #snapRectangle(Request, int, PrecisionRectangle[], PrecisionRectangle)} * . The intended usage of this method is when dragging one or more parts in * a diagram. * <P> * The returned value should be a subset of the given snapDirections based * on what correction was applied to the result. e.g., if the <b>x</b> value * was adjusted, the returned value should not contain WEST, EAST, or * HORIZONTAL. * <P> * All coordinate information received and returned by this method should be * in absolute coordinates. * * @param request * the request or <code>null</code> * @param snapLocations * the types of snapping to perform * @param rects * an array of one or more rectangles used to perform the * snapping * @param result * the correction will be applied to this point * @return the remaining snap locations */ public int snapPoint(Request request, int snapLocations, PrecisionRectangle rects[], PrecisionPoint result) { PrecisionRectangle resultRect = new PrecisionRectangle(); snapLocations = snapRectangle(request, snapLocations, rects, resultRect); result.setPreciseX(result.preciseX() + resultRect.preciseX()); result.setPreciseY(result.preciseY() + resultRect.preciseY()); return snapLocations; }
/** * @see #getPoint(int, int) * @param point * Return the index of the nearest point on the curve for this point. * @return The index of the nearest point on the curve for this point. */ private Point getPoint(Point point) { int index = getPoint(point.x, point.y); return new PrecisionPoint(Px[index], Py[index]); }