예제 #1
0
        public void Paint(IGraphicsContext3D g, Altaxo.Graph.IPaintContext paintContext, bool bForPreview)
        {
            //_isStructureInSync = false;
            _isMeasureInSync = false; // Change: interpret text every time in order to update plot items and \ID

            if (!_isStructureInSync)
            {
                // this.Interpret(g);
                InterpretText();

                _isStructureInSync = true;
                _isMeasureInSync   = false;
            }

            using (var fontCache = new FontCache())
            {
                if (!_isMeasureInSync)
                {
                    // this.MeasureStructure(g, obj);
                    MeasureGlyphs(g, fontCache, paintContext);

                    MeasureBackground(g, _rootNode.SizeX, _rootNode.SizeY, _rootNode.SizeZ);

                    _isMeasureInSync = true;
                }

                _cachedSymbolPositions.Clear();

                var gs = g.SaveGraphicsState();

                var bounds = Bounds;

                var transformmatrix = Matrix4x3.NewScalingShearingRotationDegreesTranslation(
                    ScaleX, ScaleY, ScaleZ,
                    ShearX, ShearY, ShearZ,
                    RotationX, RotationY, RotationZ,
                    _location.AbsolutePivotPositionX, _location.AbsolutePivotPositionY, _location.AbsolutePivotPositionZ);
                transformmatrix.TranslatePrepend(bounds.X, bounds.Y, bounds.Z);

                if (!bForPreview)
                {
                    TransformGraphics(g);
                    g.TranslateTransform(bounds.X, bounds.Y, bounds.Z);
                }

                // first of all paint the background
                PaintBackground(g);

                var dc = new DrawContext
                {
                    FontCache              = fontCache,
                    bForPreview            = bForPreview,
                    LinkedObject           = Altaxo.Main.AbsoluteDocumentPath.GetRootNodeImplementing <HostLayer>(this),
                    transformMatrix        = transformmatrix,
                    _cachedSymbolPositions = _cachedSymbolPositions
                };
                DrawGlyphs(g, dc, _cachedTextOffset.X, _cachedTextOffset.Y, _cachedTextOffset.Z);
                g.RestoreGraphicsState(gs);
            }
        }
예제 #2
0
        public override void Paint(IGraphicsContext3D g, IPaintContext context)
        {
            var gs = g.SaveGraphicsState();

            g.PrependTransform(_transformation);
            g.DrawLine(_linePen, Bounds.Location, Bounds.LocationPlusSize);
            g.RestoreGraphicsState(gs);
        }
예제 #3
0
        public RectangleD3D PaintSymbol(IGraphicsContext3D g, RectangleD3D bounds)
        {
            if (IsVisible)
            {
                var gs = g.SaveGraphicsState();
                g.TranslateTransform((VectorD3D)bounds.Center);
                var halfwidth = bounds.SizeX / 2;
                var symsize   = _symbolSize;

                if (_useSymbolGap)
                {
                    // plot a line with the length of symbolsize from
                    PaintLine(g, new PointD3D(-halfwidth, 0, 0), new PointD3D(-symsize, 0, 0));
                    PaintLine(g, new PointD3D(symsize, 0, 0), new PointD3D(halfwidth, 0, 0));
                }
                else // no gap
                {
                    PaintLine(g, new PointD3D(-halfwidth, 0, 0), new PointD3D(halfwidth, 0, 0));
                }
                g.RestoreGraphicsState(gs);
            }

            return(bounds);
        }
예제 #4
0
파일: LineShape.cs 프로젝트: Altaxo/Altaxo
		public override void Paint(IGraphicsContext3D g, IPaintContext context)
		{
			var gs = g.SaveGraphicsState();
			g.PrependTransform(_transformation);
			g.DrawLine(_linePen, Bounds.Location, Bounds.LocationPlusSize);
			g.RestoreGraphicsState(gs);
		}
예제 #5
0
        /// <summary>
        /// Paints the axis style labels.
        /// </summary>
        /// <param name="g">Graphics environment.</param>
        /// <param name="coordSyst">The coordinate system. Used to get the path along the axis.</param>
        /// <param name="scale">Scale.</param>
        /// <param name="tickSpacing">If not <c>null</c>, this parameter provides a custom tick spacing that is used instead of the default tick spacing of the scale.</param>
        /// <param name="styleInfo">Information about begin of axis, end of axis.</param>
        /// <param name="outerDistance">Distance between axis and labels.</param>
        /// <param name="useMinorTicks">If true, minor ticks are shown.</param>
        public virtual void Paint(IGraphicsContext3D g, G3DCoordinateSystem coordSyst, Scale scale, TickSpacing tickSpacing, CSAxisInformation styleInfo, double outerDistance, bool useMinorTicks)
        {
            _cachedAxisStyleInfo = styleInfo;
            CSLineID    styleID = styleInfo.Identifier;
            Scale       raxis   = scale;
            TickSpacing ticking = tickSpacing;

            var math = Matrix4x3.Identity;

            Logical3D r0 = styleID.GetLogicalPoint(styleInfo.LogicalValueAxisOrg);
            Logical3D r1 = styleID.GetLogicalPoint(styleInfo.LogicalValueAxisEnd);

            Logical3D outer;

            double[]        relpositions;
            AltaxoVariant[] ticks;
            if (useMinorTicks)
            {
                relpositions = ticking.GetMinorTicksNormal(raxis);
                ticks        = ticking.GetMinorTicksAsVariant();
            }
            else
            {
                relpositions = ticking.GetMajorTicksNormal(raxis);
                ticks        = ticking.GetMajorTicksAsVariant();
            }

            if (!_suppressedLabels.IsEmpty)
            {
                var filteredTicks        = new List <AltaxoVariant>();
                var filteredRelPositions = new List <double>();

                for (int i = 0; i < ticks.Length; i++)
                {
                    if (_suppressedLabels.ByValues.Contains(ticks[i]))
                    {
                        continue;
                    }
                    if (_suppressedLabels.ByNumbers.Contains(i))
                    {
                        continue;
                    }
                    if (_suppressedLabels.ByNumbers.Contains(i - ticks.Length))
                    {
                        continue;
                    }

                    filteredTicks.Add(ticks[i]);
                    filteredRelPositions.Add(relpositions[i]);
                }
                ticks        = filteredTicks.ToArray();
                relpositions = filteredRelPositions.ToArray();
            }

            IMeasuredLabelItem[] labels = _labelFormatting.GetMeasuredItems(g, _font, ticks);

            double     emSize        = _font.Size;
            CSAxisSide labelSide     = null != _labelSide ? _labelSide.Value : styleInfo.PreferredLabelSide;
            var        labelOutlines = new RectangularObjectOutline[ticks.Length];

            for (int i = 0; i < ticks.Length; i++)
            {
                double r = relpositions[i];

                if (!Altaxo.Calc.RMath.IsInIntervalCC(r, -1000, 1000))
                {
                    continue;
                }

                outer = coordSyst.GetLogicalDirection(styleID.ParallelAxisNumber, labelSide);
                PointD3D tickorg = coordSyst.GetPositionAndNormalizedDirection(r0, r1, r, outer, out var outVector);
                PointD3D tickend = tickorg + outVector * outerDistance;

                var msize = labels[i].Size;
                var morg  = tickend;

                if (_automaticRotationShift)
                {
                    // if this option is choosen, we have to find a shift value that shifts the center of the text outwards so that the bounding box of the text will not cross the plane that is
                    // defined by the tickend point and the normal vector outVector

                    // Assume that the text is now centered x, y, and z around the point tickend (but here we use origin instead tickend)
                    math = Matrix4x3.NewRotation(_rotationX, _rotationY, _rotationZ);
                    // we have to find all points with negative distance to the plane spanned by tickend and the vector outVector (but again instead of tickend we use origin)
                    var msizePad = msize + new VectorD3D(
                        (_font.Size * 1) / 3,                                            // whereas above and below text no padding is neccessary, it is optically nicer to have left and right padding of the string by 1/6 of font size.
                        0,
                        (_font.Size * 1) / 3                                             // same padding applies to z
                        );
                    var crect = new RectangleD3D((PointD3D)(-0.5 * msizePad), msizePad); // our text centered around origin

                    double shift = 0;
                    foreach (PointD3D p in crect.Vertices)
                    {
                        PointD3D ps       = math.Transform(p);
                        double   distance = Math3D.GetDistancePointToPlane(ps, PointD3D.Empty, outVector);
                        if (-distance > shift)
                        {
                            shift = -distance; // only negative distances will count here
                        }
                    }
                    morg += outVector * shift;
                }
                else
                {
                    morg = morg.WithXPlus(outVector.X * _font.Size / 3);
                }

                var mrect = new RectangleD3D(morg, msize);
                if (_automaticRotationShift)
                {
                    mrect = AdjustRectangle(mrect, Alignment.Center, Alignment.Center, Alignment.Center);
                }
                else
                {
                    mrect = AdjustRectangle(mrect, _alignmentX, _alignmentY, _alignmentZ);
                }

                math = Matrix4x3.Identity;
                math.TranslatePrepend(morg.X, morg.Y, morg.Z);

                if (_rotationZ != 0)
                {
                    math.RotationZDegreePrepend(_rotationZ);
                }
                if (_rotationY != 0)
                {
                    math.RotationYDegreePrepend(_rotationY);
                }
                if (_rotationX != 0)
                {
                    math.RotationXDegreePrepend(_rotationX);
                }

                math.TranslatePrepend((mrect.X - morg.X + emSize * _offsetX), (mrect.Y - morg.Y + emSize * _offsetY), (mrect.Z - morg.Z + emSize * _offsetZ));

                var gs = g.SaveGraphicsState();
                g.PrependTransform(math);

                if (_backgroundStyle != null)
                {
                    var itemRectangle = new RectangleD3D(PointD3D.Empty, msize);
                    _backgroundStyle.Measure(itemRectangle);
                    _backgroundStyle.Draw(g, itemRectangle);
                }

                labels[i].Draw(g, _brush, PointD3D.Empty);
                labelOutlines[i] = new RectangularObjectOutline(new RectangleD3D(PointD3D.Empty, msize), math);
                g.RestoreGraphicsState(gs); // Restore the graphics state
            }

            _cachedLabelOutlines = labelOutlines;
        }
예제 #6
0
		public void Paint(IGraphicsContext3D g, Altaxo.Graph.IPaintContext paintContext, bool bForPreview)
		{
			//_isStructureInSync = false;
			_isMeasureInSync = false;  // Change: interpret text every time in order to update plot items and \ID

			if (!this._isStructureInSync)
			{
				// this.Interpret(g);
				this.InterpretText();

				_isStructureInSync = true;
				_isMeasureInSync = false;
			}

			using (FontCache fontCache = new FontCache())
			{
				if (!this._isMeasureInSync)
				{
					// this.MeasureStructure(g, obj);
					this.MeasureGlyphs(g, fontCache, paintContext);

					MeasureBackground(g, _rootNode.SizeX, _rootNode.SizeY, _rootNode.SizeZ);

					_isMeasureInSync = true;
				}

				_cachedSymbolPositions.Clear();

				var gs = g.SaveGraphicsState();

				var bounds = Bounds;

				var transformmatrix = Matrix4x3.NewScalingShearingRotationDegreesTranslation(
				ScaleX, ScaleY, ScaleZ,
					ShearX, ShearY, ShearZ,
					RotationX, RotationY, RotationZ,
					_location.AbsolutePivotPositionX, _location.AbsolutePivotPositionY, _location.AbsolutePivotPositionZ);
				transformmatrix.TranslatePrepend(bounds.X, bounds.Y, bounds.Z);

				if (!bForPreview)
				{
					TransformGraphics(g);
					g.TranslateTransform(bounds.X, bounds.Y, bounds.Z);
				}

				// first of all paint the background
				PaintBackground(g);

				DrawContext dc = new DrawContext();
				dc.FontCache = fontCache;
				dc.bForPreview = bForPreview;
				dc.LinkedObject = Altaxo.Main.AbsoluteDocumentPath.GetRootNodeImplementing<HostLayer>(this);
				dc.transformMatrix = transformmatrix;
				dc._cachedSymbolPositions = _cachedSymbolPositions;
				DrawGlyphs(g, dc, _cachedTextOffset.X, _cachedTextOffset.Y, _cachedTextOffset.Z);
				g.RestoreGraphicsState(gs);
			}
		}
예제 #7
0
		/// <summary>
		/// Paints the axis style labels.
		/// </summary>
		/// <param name="g">Graphics environment.</param>
		/// <param name="coordSyst">The coordinate system. Used to get the path along the axis.</param>
		/// <param name="scale">Scale.</param>
		/// <param name="tickSpacing">If not <c>null</c>, this parameter provides a custom tick spacing that is used instead of the default tick spacing of the scale.</param>
		/// <param name="styleInfo">Information about begin of axis, end of axis.</param>
		/// <param name="outerDistance">Distance between axis and labels.</param>
		/// <param name="useMinorTicks">If true, minor ticks are shown.</param>
		public virtual void Paint(IGraphicsContext3D g, G3DCoordinateSystem coordSyst, Scale scale, TickSpacing tickSpacing, CSAxisInformation styleInfo, double outerDistance, bool useMinorTicks)
		{
			_cachedAxisStyleInfo = styleInfo;
			CSLineID styleID = styleInfo.Identifier;
			Scale raxis = scale;
			TickSpacing ticking = tickSpacing;

			var math = Matrix4x3.Identity;

			Logical3D r0 = styleID.GetLogicalPoint(styleInfo.LogicalValueAxisOrg);
			Logical3D r1 = styleID.GetLogicalPoint(styleInfo.LogicalValueAxisEnd);

			VectorD3D outVector;
			Logical3D outer;

			double[] relpositions;
			AltaxoVariant[] ticks;
			if (useMinorTicks)
			{
				relpositions = ticking.GetMinorTicksNormal(raxis);
				ticks = ticking.GetMinorTicksAsVariant();
			}
			else
			{
				relpositions = ticking.GetMajorTicksNormal(raxis);
				ticks = ticking.GetMajorTicksAsVariant();
			}

			if (!_suppressedLabels.IsEmpty)
			{
				List<AltaxoVariant> filteredTicks = new List<AltaxoVariant>();
				List<double> filteredRelPositions = new List<double>();

				for (int i = 0; i < ticks.Length; i++)
				{
					if (_suppressedLabels.ByValues.Contains(ticks[i]))
						continue;
					if (_suppressedLabels.ByNumbers.Contains(i))
						continue;
					if (_suppressedLabels.ByNumbers.Contains(i - ticks.Length))
						continue;

					filteredTicks.Add(ticks[i]);
					filteredRelPositions.Add(relpositions[i]);
				}
				ticks = filteredTicks.ToArray();
				relpositions = filteredRelPositions.ToArray();
			}

			IMeasuredLabelItem[] labels = _labelFormatting.GetMeasuredItems(g, _font, ticks);

			double emSize = _font.Size;
			CSAxisSide labelSide = null != _labelSide ? _labelSide.Value : styleInfo.PreferredLabelSide;
			var labelOutlines = new RectangularObjectOutline[ticks.Length];
			for (int i = 0; i < ticks.Length; i++)
			{
				double r = relpositions[i];

				if (!Altaxo.Calc.RMath.IsInIntervalCC(r, -1000, 1000))
					continue;

				outer = coordSyst.GetLogicalDirection(styleID.ParallelAxisNumber, labelSide);
				PointD3D tickorg = coordSyst.GetPositionAndNormalizedDirection(r0, r1, r, outer, out outVector);
				PointD3D tickend = tickorg + outVector * outerDistance;

				var msize = labels[i].Size;
				var morg = tickend;

				if (_automaticRotationShift)
				{
					// if this option is choosen, we have to find a shift value that shifts the center of the text outwards so that the bounding box of the text will not cross the plane that is
					// defined by the tickend point and the normal vector outVector

					// Assume that the text is now centered x, y, and z around the point tickend (but here we use origin instead tickend)
					math = Matrix4x3.NewRotation(_rotationX, _rotationY, _rotationZ);
					// we have to find all points with negative distance to the plane spanned by tickend and the vector outVector (but again instead of tickend we use origin)
					var msizePad = msize + new VectorD3D(
					(_font.Size * 1) / 3, // whereas above and below text no padding is neccessary, it is optically nicer to have left and right padding of the string by 1/6 of font size.
					0,
					(_font.Size * 1) / 3 // same padding applies to z
					);
					var crect = new RectangleD3D((PointD3D)(-0.5 * msizePad), msizePad); // our text centered around origin

					double shift = 0;
					foreach (PointD3D p in crect.Vertices)
					{
						PointD3D ps = math.Transform(p);
						double distance = Math3D.GetDistancePointToPlane(ps, PointD3D.Empty, outVector);
						if (-distance > shift)
							shift = -distance; // only negative distances will count here
					}
					morg += outVector * shift;
				}
				else
				{
					morg = morg.WithXPlus(outVector.X * _font.Size / 3);
				}

				var mrect = new RectangleD3D(morg, msize);
				if (_automaticRotationShift)
					mrect = AdjustRectangle(mrect, Alignment.Center, Alignment.Center, Alignment.Center);
				else
					mrect = AdjustRectangle(mrect, _alignmentX, _alignmentY, _alignmentZ);

				math = Matrix4x3.Identity;
				math.TranslatePrepend(morg.X, morg.Y, morg.Z);

				if (this._rotationZ != 0)
					math.RotationZDegreePrepend(this._rotationZ);
				if (this._rotationY != 0)
					math.RotationYDegreePrepend(this._rotationY);
				if (this._rotationX != 0)
					math.RotationXDegreePrepend(this._rotationX);

				math.TranslatePrepend((mrect.X - morg.X + emSize * _offsetX), (mrect.Y - morg.Y + emSize * _offsetY), (mrect.Z - morg.Z + emSize * _offsetZ));

				var gs = g.SaveGraphicsState();
				g.PrependTransform(math);

				if (this._backgroundStyle != null)
				{
					var itemRectangle = new RectangleD3D(PointD3D.Empty, msize);
					_backgroundStyle.Measure(itemRectangle);
					_backgroundStyle.Draw(g, itemRectangle);
				}

				labels[i].Draw(g, _brush, PointD3D.Empty);
				labelOutlines[i] = new RectangularObjectOutline(new RectangleD3D(PointD3D.Empty, msize), math);
				g.RestoreGraphicsState(gs); // Restore the graphics state
			}

			_cachedLabelOutlines = labelOutlines;
		}
예제 #8
0
		public void Paint(IGraphicsContext3D g, IPlotArea layer, Processed3DPlotData pdata, Processed3DPlotData prevItemData, Processed3DPlotData nextItemData)
		{
			if (this._labelColumnProxy.Document == null)
				return;

			if (null != _attachedPlane)
				_attachedPlane = layer.UpdateCSPlaneID(_attachedPlane);

			PlotRangeList rangeList = pdata.RangeList;
			var ptArray = pdata.PlotPointsInAbsoluteLayerCoordinates;
			Altaxo.Data.IReadableColumn labelColumn = this._labelColumnProxy.Document;

			bool isUsingVariableColorForLabelText = null != _cachedColorForIndexFunction && IsColorReceiver;
			bool isUsingVariableColorForLabelBackground = null != _cachedColorForIndexFunction &&
				(null != _backgroundStyle && _backgroundStyle.SupportsUserDefinedMaterial && (_backgroundColorLinkage == ColorLinkage.Dependent || _backgroundColorLinkage == ColorLinkage.PreserveAlpha));
			bool isUsingVariableColor = isUsingVariableColorForLabelText || isUsingVariableColorForLabelBackground;
			IMaterial clonedTextBrush = _material;
			IMaterial clonedBackBrush = null;
			if (isUsingVariableColorForLabelBackground)
				clonedBackBrush = _backgroundStyle.Material;

			// save the graphics stat since we have to translate the origin
			var gs = g.SaveGraphicsState();

			double xpos = 0, ypos = 0, zpos = 0;
			double xpre, ypre, zpre;
			double xdiff, ydiff, zdiff;

			bool isFormatStringContainingBraces = _labelFormatString?.IndexOf('{') >= 0;
			var culture = System.Threading.Thread.CurrentThread.CurrentCulture;

			bool mustUseLogicalCoordinates = null != this._attachedPlane || 0 != _cachedLogicalShiftX || 0 != _cachedLogicalShiftY || 0 != _cachedLogicalShiftZ;

			for (int r = 0; r < rangeList.Count; r++)
			{
				int lower = rangeList[r].LowerBound;
				int upper = rangeList[r].UpperBound;
				int offset = rangeList[r].OffsetToOriginal;
				for (int j = lower; j < upper; j += _skipFrequency)
				{
					string label;
					if (string.IsNullOrEmpty(_labelFormatString))
					{
						label = labelColumn[j + offset].ToString();
					}
					else if (!isFormatStringContainingBraces)
					{
						label = labelColumn[j + offset].ToString(_labelFormatString, culture);
					}
					else
					{
						// the label format string can contain {0} for the label column item, {1} for the row index, {2} .. {4} for the x, y and z component of the data point
						label = string.Format(_labelFormatString, labelColumn[j + offset], j + offset, pdata.GetPhysical(0, j + offset), pdata.GetPhysical(1, j + offset), pdata.GetPhysical(2, j + offset));
					}

					if (string.IsNullOrEmpty(label))
						continue;

					double localSymbolSize = _symbolSize;
					if (null != _cachedSymbolSizeForIndexFunction)
					{
						localSymbolSize = _cachedSymbolSizeForIndexFunction(j + offset);
					}

					double localFontSize = _fontSizeOffset + _fontSizeFactor * localSymbolSize;
					if (!(localFontSize > 0))
						continue;

					_font = _font.WithSize(localFontSize);

					// Start of preparation of brushes, if a variable color is used
					if (isUsingVariableColor)
					{
						var c = _cachedColorForIndexFunction(j + offset);

						if (isUsingVariableColorForLabelText)
						{
							clonedTextBrush = clonedTextBrush.WithColor(new NamedColor(AxoColor.FromArgb(c.A, c.R, c.G, c.B), "e"));
						}
						if (isUsingVariableColorForLabelBackground)
						{
							if (_backgroundColorLinkage == ColorLinkage.PreserveAlpha)
								clonedBackBrush = clonedBackBrush.WithColor(new NamedColor(AxoColor.FromArgb(clonedBackBrush.Color.Color.A, c.R, c.G, c.B), "e"));
							else
								clonedBackBrush = clonedBackBrush.WithColor(new NamedColor(AxoColor.FromArgb(c.A, c.R, c.G, c.B), "e"));
						}
					}
					// end of preparation of brushes for variable colors

					if (mustUseLogicalCoordinates) // we must use logical coordinates because either there is a shift of logical coordinates, or an attached plane
					{
						Logical3D r3d = layer.GetLogical3D(pdata, j + offset);
						r3d.RX += _cachedLogicalShiftX;
						r3d.RY += _cachedLogicalShiftY;
						r3d.RZ += _cachedLogicalShiftZ;

						if (null != this._attachedPlane)
						{
							var pp = layer.CoordinateSystem.GetPointOnPlane(this._attachedPlane, r3d);
							xpre = pp.X;
							ypre = pp.Y;
							zpre = pp.Z;
						}
						else
						{
							PointD3D pt;
							layer.CoordinateSystem.LogicalToLayerCoordinates(r3d, out pt);
							xpre = pt.X;
							ypre = pt.Y;
							zpre = pt.Z;
						}
					}
					else // no shifting, thus we can use layer coordinates
					{
						xpre = ptArray[j].X;
						ypre = ptArray[j].Y;
						zpre = ptArray[j].Z;
					}

					xdiff = xpre - xpos;
					ydiff = ypre - ypos;
					zdiff = zpre - zpos;
					xpos = xpre;
					ypos = ypre;
					zpos = zpre;
					g.TranslateTransform(xdiff, ydiff, zdiff);
					g.RotateTransform(_rotationX, _rotationY, _rotationZ);

					this.Paint(g, label, localSymbolSize, clonedTextBrush, clonedBackBrush);

					g.RotateTransform(-_rotationX, -_rotationY, -_rotationZ);
				} // end for
			}

			g.RestoreGraphicsState(gs); // Restore the graphics state
		}
예제 #9
0
		public RectangleD3D PaintSymbol(IGraphicsContext3D g, RectangleD3D bounds)
		{
			if (IsVisible)
			{
				var gs = g.SaveGraphicsState();
				g.TranslateTransform((VectorD3D)bounds.Center);
				var halfwidth = bounds.SizeX / 2;
				var symsize = _symbolSize;

				if (_useSymbolGap)
				{
					// plot a line with the length of symbolsize from
					PaintLine(g, new PointD3D(-halfwidth, 0, 0), new PointD3D(-symsize, 0, 0));
					PaintLine(g, new PointD3D(symsize, 0, 0), new PointD3D(halfwidth, 0, 0));
				}
				else // no gap
				{
					PaintLine(g, new PointD3D(-halfwidth, 0, 0), new PointD3D(halfwidth, 0, 0));
				}
				g.RestoreGraphicsState(gs);
			}

			return bounds;
		}