/// <summary> /// Creates DwellOnIdentifierEventArgs instance with read-only values set /// </summary> /// <param name="identifier"></param> /// <param name="identifierRange"></param> /// <param name="visibleRange"></param> /// <param name="visibleText"></param> public DwellOnIdentifierEventArgs(string identifier, CharacterRange identifierRange, CharacterRange visibleRange, string visibleText) { Identifier = identifier; IdentifierRange = identifierRange; VisibleRange = visibleRange; VisibleText = visibleText; }
private static List <PatchSection> CombineSections( List <PatchSection> sections1, List <PatchSection> sections2, PatchSection exceptSection1, PatchSection exceptSection2, List <PatchVertex> vertices1, List <PatchVertex> vertices2, List <int> path1, List <int> path2, List <int> combinedPath, Dictionary <PointF, int> p2i) { List <PatchSection> sections = new List <PatchSection>(); foreach (var sec in sections1) { if (sec == exceptSection1) { continue; } // 切り口の端点が新しいメッシュ内でどこにあるか sections.Add(GetMappedSection(sec, vertices1, path1, combinedPath, p2i)); } foreach (var sec in sections2) { if (sec == exceptSection2) { continue; } // 切り口の端点が新しいメッシュ内でどこにあるか sections.Add(GetMappedSection(sec, vertices2, path2, combinedPath, p2i)); } return(sections); }
static float SectionHeight(PatchSkeletalMesh mesh, PatchSection section, PatchSkeletonBone b) { List <PointF> path = GetPath(mesh).Select(v => v.position).ToList(); var curves = SectionToAdjuscentCurves(path, section, 5, 30); if (path == null || curves == null || b == null) { return(0); } if (curves.Item1 == null || curves.Item2 == null) { return(0); } float height = 0; float boneLen = FMath.Distance(b.src.position, b.dst.position); if (boneLen <= 1e-4) { return(0); } PointF x, y; CalcBoneCoordinate(b, out x, out y); // 各セグメントのボーンからのズレを求める int cnt = Math.Min(curves.Item1.Length, curves.Item2.Length); for (int i = 0; i < cnt; i++) { int idx1 = curves.Item1.First + curves.Item1.Length - 1 - i; int idx2 = curves.Item2.First + i; while (idx1 < 0) { idx1 += path.Count; } while (idx2 < 0) { idx2 += path.Count; } PointF pt1 = path[idx1 % path.Count]; PointF pt2 = path[idx2 % path.Count]; pt1.X -= b.src.position.X; pt1.Y -= b.src.position.Y; pt2.X -= b.src.position.X; pt2.Y -= b.src.position.Y; height += pt1.X * y.X + pt1.Y * y.Y; height += pt2.X * y.X + pt2.Y * y.Y; } height /= 2 * cnt; return(height); }
/// <param name="checkLength">sectionから何pixel分の輪郭を計算に使うか/param> static float SectionHeight(PatchSkeletalMesh mesh, PatchSection section, PatchSkeletonBone b, float curveLength) { List <PatchVertex> path = GetPath(mesh); var rawcurves = section2adjCurves(path, section); if (path == null || rawcurves == null || b == null) { return(0); } var curves = TrimCurves(path, rawcurves, 0, curveLength); if (curves.Item1 == null || curves.Item2 == null) { return(0); } float boneLen = FMath.Distance(b.src.position, b.dst.position); if (boneLen <= 1e-4) { return(0); } PointF x, y; BoneCoordinate(b, out x, out y); // 各セグメントのボーンからのズレを求める float height = 0; int cnt = Math.Min(curves.Item1.Length, curves.Item2.Length); for (int i = 0; i < cnt; i++) { int idx1 = curves.Item1.First + curves.Item1.Length - 1 - i; int idx2 = curves.Item2.First + i; while (idx1 < 0) { idx1 += path.Count; } while (idx2 < 0) { idx2 += path.Count; } PointF pt1 = path[idx1 % path.Count].position; PointF pt2 = path[idx2 % path.Count].position; pt1.X -= b.src.position.X; pt1.Y -= b.src.position.Y; pt2.X -= b.src.position.X; pt2.Y -= b.src.position.Y; height += pt1.X * y.X + pt1.Y * y.Y; height += pt2.X * y.X + pt2.Y * y.Y; } height /= 2 * cnt; return(height); }
public static SizeF MeasureString(Graphics g, string str, Font font, Rectangle rect, StringFormat sf) { var sfTemp = sf.Clone() as StringFormat; var ranges = new CharacterRange[] { new CharacterRange(0, str.Length) }; sfTemp.SetMeasurableCharacterRanges(ranges); var regions = g.MeasureCharacterRanges(str, font, rect, sfTemp); if (regions != null && regions.Length > 0) return regions[0].GetBounds(g).Size; return new SizeF(); }
public Word(CharacterRange range, string text) { if (text == null) throw new ArgumentNullException("text"); Debug.Assert(text.Length > 0); _range = range; _text = text; }
private int findCharacterPositionInPart(DocumentRow row, StylizedRowElement atom, float offset) { int farLength = atom.range.Length; int nearLength = 0; CharacterRange range = new CharacterRange(0, farLength); string test = row.Raw.Substring(range.First, range.Length); float nearSize = 0; float farSize = MeasureStringPart(range, test, atom.style).Size.Width; if (nearSize > offset) return nearLength; if (farSize < offset) return farLength - 1; while (farLength > nearLength) { if (farSize < offset) { break; } if (nearSize > offset) { farLength = nearLength; break; } range = new CharacterRange(0, nearLength + (farLength - nearLength) / 2); test = row.Raw.Substring(range.First, range.Length); float middle = MeasureStringPart(range, test, atom.style).Size.Width; if (middle < offset) { if (nearLength == range.Length) { return farSize - middle > middle - nearSize ? nearLength : farLength; } nearLength = range.Length; nearSize = middle; } else { if (farLength == range.Length) { return farSize - middle > middle - nearSize ? nearLength : farLength; } farLength = range.Length; farSize = middle; } } return farLength; }
private EMFStringFormat(byte[] RecordData) { ObjectType = EmfObjectType.stringformat; myStringFormat = new System.Drawing.StringFormat(); //put the Data into a stream and use a binary reader to read the data MemoryStream _ms = new MemoryStream(RecordData); BinaryReader _br = new BinaryReader(_ms); _br.ReadUInt32(); //Who cares about version..not me! myStringFormat.FormatFlags = (StringFormatFlags)_br.ReadUInt32(); _br.ReadBytes(4); //Language...Ignore for now! myStringFormat.LineAlignment = (StringAlignment)_br.ReadUInt32(); myStringFormat.Alignment = (StringAlignment)_br.ReadUInt32(); UInt32 DigitSubstitutionMethod = _br.ReadUInt32(); UInt32 DigitSubstitutionLanguage = _br.ReadUInt32(); myStringFormat.SetDigitSubstitution((int)DigitSubstitutionLanguage, (StringDigitSubstitute)DigitSubstitutionMethod); Single FirstTabOffSet = _br.ReadSingle(); myStringFormat.HotkeyPrefix = (System.Drawing.Text.HotkeyPrefix)_br.ReadInt32(); _br.ReadSingle(); //leading Margin _br.ReadSingle(); //trailingMargin _br.ReadSingle(); //tracking myStringFormat.Trimming = (StringTrimming)_br.ReadUInt32(); Int32 TabStopCount = _br.ReadInt32(); Int32 RangeCount = _br.ReadInt32(); //Next is stringformatdata... Single[] TabStopArray; System.Drawing.CharacterRange[] RangeArray; if (TabStopCount > 0) { TabStopArray = new Single[TabStopCount]; for (int i = 0; i < TabStopCount; i++) { TabStopArray[i] = _br.ReadSingle(); } myStringFormat.SetTabStops(FirstTabOffSet, TabStopArray); } if (RangeCount > 0) { RangeArray = new System.Drawing.CharacterRange[RangeCount]; for (int i = 0; i < RangeCount; i++) { RangeArray[i].First = _br.ReadInt32(); RangeArray[i].Length = _br.ReadInt32(); } myStringFormat.SetMeasurableCharacterRanges(RangeArray); } }
private static SizeF GetTextSize(string str, Graphics g, bool fTitle) { SizeF empty = SizeF.Empty; CharacterRange[] ranges = new CharacterRange[] { new CharacterRange(0, str.Length) }; sfMeasure.SetMeasurableCharacterRanges(ranges); Region[] regionArray = g.MeasureCharacterRanges(str, fTitle ? font : fontSubText, rctMeasure, sfMeasure); using(regionArray[0]) { empty = regionArray[0].GetBounds(g).Size; empty.Width += 6f; } return empty; }
//----------------------------------------------------------------------------------- // FindConnectingSections() //----------------------------------------------------------------------------------- /// <summary> /// skl内の同一のボーンと交差していて、なおかつ向きが逆の切り口を探す /// </summary> private static bool FindConnectingSections( PatchSkeletalMesh smesh1, PatchSkeletalMesh smesh2, PatchSkeleton skl, out PatchSection section1, out PatchSection section2, out PatchSkeletonBone crossingBone) { section1 = new PatchSection(0, -1); section2 = new PatchSection(0, -1); crossingBone = null; if (skl == null) { return(false); } if (smesh1 == null || smesh2 == null) { return(false); } foreach (var bone in skl.bones) { List <PatchSection> sections1, sections2; List <float> dir1, dir2; // 各切り口がboneと交差しているか if (!FindCrossingSection(smesh1, bone, out sections1, out dir1)) { continue; } if (!FindCrossingSection(smesh2, bone, out sections2, out dir2)) { continue; } for (int i = 0; i < sections1.Count; i++) { for (int j = 0; j < sections2.Count; j++) { // 2つの切り口が逆向きか if (dir1[i] * dir2[j] >= 0) { continue; } section1 = sections1[i]; section2 = sections2[j]; crossingBone = bone; return(true); } } } return(false); }
private EMFStringFormat(byte[] RecordData) { ObjectType = EmfObjectType.stringformat; myStringFormat = new System.Drawing.StringFormat(); //put the Data into a stream and use a binary reader to read the data MemoryStream _ms = new MemoryStream(RecordData); BinaryReader _br = new BinaryReader(_ms); _br.ReadUInt32(); //Who cares about version..not me! myStringFormat.FormatFlags = (StringFormatFlags)_br.ReadUInt32(); _br.ReadBytes(4);//Language...Ignore for now! myStringFormat.LineAlignment = (StringAlignment)_br.ReadUInt32(); myStringFormat.Alignment = (StringAlignment)_br.ReadUInt32(); UInt32 DigitSubstitutionMethod = _br.ReadUInt32(); UInt32 DigitSubstitutionLanguage = _br.ReadUInt32(); myStringFormat.SetDigitSubstitution((int)DigitSubstitutionLanguage, (StringDigitSubstitute)DigitSubstitutionMethod); Single FirstTabOffSet = _br.ReadSingle(); myStringFormat.HotkeyPrefix = (System.Drawing.Text.HotkeyPrefix) _br.ReadInt32(); _br.ReadSingle();//leading Margin _br.ReadSingle();//trailingMargin _br.ReadSingle();//tracking myStringFormat.Trimming = (StringTrimming)_br.ReadUInt32(); Int32 TabStopCount = _br.ReadInt32(); Int32 RangeCount = _br.ReadInt32(); //Next is stringformatdata... Single[] TabStopArray; System.Drawing.CharacterRange[] RangeArray; if (TabStopCount > 0) { TabStopArray = new Single[TabStopCount]; for (int i = 0; i < TabStopCount; i++) { TabStopArray[i] = _br.ReadSingle(); } myStringFormat.SetTabStops(FirstTabOffSet, TabStopArray); } if (RangeCount > 0) { RangeArray = new System.Drawing.CharacterRange[RangeCount]; for (int i = 0; i < RangeCount; i++) { RangeArray[i].First = _br.ReadInt32(); RangeArray[i].Length = _br.ReadInt32(); } myStringFormat.SetMeasurableCharacterRanges(RangeArray); } }
public static Size GetTextSize(Graphics graphics, string text, Font font, Size size) { if (text.Length == 0) { return Size.Empty; } StringFormat stringFormat = new StringFormat(); stringFormat.FormatFlags = StringFormatFlags.FitBlackBox; RectangleF layoutRect = new RectangleF(0f, 0f, size.Width, size.Height); CharacterRange[] ranges = new CharacterRange[] {new CharacterRange(0, text.Length)}; Region[] regionArray = new Region[1]; stringFormat.SetMeasurableCharacterRanges(ranges); Rectangle rectangle = Rectangle.Round(graphics.MeasureCharacterRanges(text, font, layoutRect, stringFormat)[0].GetBounds(graphics)); return new Size(rectangle.Width, rectangle.Height); }
public float[] GetCharacterXPositions( Graphics g, string str ) { // Setup the StringFormat with proper CharacterRange references. StringFormat testFormat = new StringFormat(); CharacterRange[] ranges = new CharacterRange[ str.Length ]; for ( int i=0; i < str.Length; i++ ) ranges[i] = new CharacterRange( i, 1 ); testFormat.SetMeasurableCharacterRanges( ranges ); // Measure into Regions Region[] regions = g.MeasureCharacterRanges( str, _font, new Rectangle( 0, 0, 1000, 1000 ), testFormat ); // Convert Regions to Rects, then X coords. float[] xCoords = regions.Select( region => region.GetBounds( g ).X ).ToArray(); return xCoords; }
// // AdjustPosition() // static void AdjustPosition( PatchSkeletalMesh smesh1, PatchSkeletalMesh smesh2, PatchSkeleton skl, PatchSection section1, PatchSection section2, PatchSkeletonBone bone) { PatchSkeletonBone refBone = null; foreach (var b in skl.bones) { if (bone == b) { refBone = b; break; } } if (refBone == null) { return; } // 切り口の中心とボーンの軸とのずれ(ボーンと垂直な方向について) float height1 = SectionHeight(smesh1, section1, refBone); float height2 = SectionHeight(smesh2, section2, refBone); // ボーンと水平なベクトルと垂直なベクトルをそれぞれx, yとして取得 PointF x, y; CalcBoneCoordinate(refBone, out x, out y); // smesh2の切り口の中心がsmesh1の切り口の中心に重なるようにsmesh2をずらす if (Math.Abs(height1 - height2) > 1e-4) { float dx = y.X * (height1 - height2); float dy = y.Y * (height1 - height2); // mesh2の頂点・制御点を平行移動する foreach (var v in smesh2.mesh.vertices) { v.position = new PointF(v.position.X + dx, v.position.Y + dy); } foreach (var c in smesh2.mesh.CopyControlPoints()) { smesh2.mesh.TranslateControlPoint(c.position, new PointF(c.position.X + dx, c.position.Y + dy), false); } } }
static PatchSection GetMappedSection(PatchSection orgsection, List <PatchVertex> vertices, List <int> orgpath, List <int> combinedPath, Dictionary <PointF, int> p2i) { int idx0 = orgpath[FMath.Rem(orgsection.First, orgpath.Count)]; PointF p0 = vertices[idx0].position; int newIdx0 = combinedPath.IndexOf(p2i[p0]); int idx1 = orgpath[FMath.Rem(orgsection.First + orgsection.Length - 1, orgpath.Count)]; PointF p1 = vertices[idx1].position; int newIdx1 = combinedPath.IndexOf(p2i[p1]); if (newIdx0 > newIdx1) { FMath.Swap(ref newIdx0, ref newIdx1); } PatchSection newSection = new PatchSection(newIdx0, newIdx1 - newIdx0 + 1); return(newSection); }
private static int getDrawLength(Graphics g, Font fnt, RectangleF rect, StringFormat sf, int maxLen) { CharacterRange[] ranges = new CharacterRange[] { new CharacterRange(0, 1) }; float num = 1f; float width = 0f; int num3 = 2; string text = StrFunc.CreateInstance().MakeCycleStr(maxLen, "X"); while ((num3 <= maxLen) && (width != num)) { num = width; ranges[0].Length = num3; sf.SetMeasurableCharacterRanges(ranges); width = g.MeasureCharacterRanges(text, fnt, rect, sf)[0].GetBounds(g).Width; num3++; } if (num3 == maxLen) { return maxLen; } return (num3 - 1); }
public void MeasureCharacterRanges_StringFormat_LineAlignment_DirectionVertical () { if (font == null) Assert.Ignore ("Couldn't create required font"); string text = "Hello Mono::"; CharacterRange[] ranges = new CharacterRange[1]; ranges[0] = new CharacterRange (5, 4); StringFormat string_format = new StringFormat (); string_format.FormatFlags = StringFormatFlags.DirectionVertical; string_format.SetMeasurableCharacterRanges (ranges); using (Bitmap bitmap = new Bitmap (20, 20)) { using (Graphics g = Graphics.FromImage (bitmap)) { string_format.LineAlignment = StringAlignment.Near; Region[] regions = g.MeasureCharacterRanges (text, font, new RectangleF (0, 0, 320, 32), string_format); Assert.AreEqual (1, regions.Length, "Near.Region"); RectangleF near = regions[0].GetBounds (g); string_format.LineAlignment = StringAlignment.Center; regions = g.MeasureCharacterRanges (text, font, new RectangleF (0, 0, 320, 32), string_format); Assert.AreEqual (1, regions.Length, "Center.Region"); RectangleF center = regions[0].GetBounds (g); string_format.LineAlignment = StringAlignment.Far; regions = g.MeasureCharacterRanges (text, font, new RectangleF (0, 0, 320, 32), string_format); Assert.AreEqual (1, regions.Length, "Far.Region"); RectangleF far = regions[0].GetBounds (g); Assert.IsTrue (near.X < center.X, "near-center/X"); Assert.AreEqual (near.Y, center.Y, 0.1, "near-center/Y"); Assert.AreEqual (near.Width, center.Width, 0.1, "near-center/Width"); Assert.AreEqual (near.Height, center.Height, 0.1, "near-center/Height"); Assert.IsTrue (center.X < far.X, "center-far/X"); Assert.AreEqual (center.Y, far.Y, 0.1, "center-far/Y"); Assert.AreEqual (center.Width, far.Width, 0.1, "center-far/Width"); Assert.AreEqual (center.Height, far.Height, 0.1, "center-far/Height"); } } }
private void MeasureCharacterRanges (string text, int first, int length) { if (font == null) Assert.Ignore ("Couldn't create required font"); CharacterRange[] ranges = new CharacterRange[1]; ranges[0] = new CharacterRange (first, length); StringFormat string_format = new StringFormat (); string_format.FormatFlags = StringFormatFlags.NoClip; string_format.SetMeasurableCharacterRanges (ranges); using (Bitmap bitmap = new Bitmap (20, 20)) { using (Graphics g = Graphics.FromImage (bitmap)) { SizeF size = g.MeasureString (text, font, new Point (0, 0), string_format); RectangleF layout_rect = new RectangleF (0.0f, 0.0f, size.Width, size.Height); g.MeasureCharacterRanges (text, font, layout_rect, string_format); } } }
public Region[] MeasureStringLine(Graphics g, string text, CharacterRange[] measureRanges, Font font, Rectangle textRect, StringAlignment align = StringAlignment.Near, StringAlignment lineAlign = StringAlignment.Center) { // Expand text rect, because DrawString stops too soon textRect.Width += DrawStringWidthAdd; textRect.Height = Math.Max(textRect.Height, font.Height); // Assume manual ellipsis textRect.Width -= 5; StringFormat nameLabelFormat = StringFormat.GenericDefault; nameLabelFormat.Alignment = align; nameLabelFormat.LineAlignment = lineAlign; nameLabelFormat.Trimming = StringTrimming.Character; nameLabelFormat.FormatFlags |= StringFormatFlags.NoWrap | StringFormatFlags.MeasureTrailingSpaces; nameLabelFormat.SetMeasurableCharacterRanges(measureRanges); return g.MeasureCharacterRanges(text, font, textRect, nameLabelFormat); }
public FaceApplied(string face, CharacterRange range) { this.face = face; this.range = range; }
//----------------------------------------------------------------------------------- // Deform() //----------------------------------------------------------------------------------- /// <summary> /// 切り口付近で2つのsmeshが重なるように変形する /// </summary> private static void Deform(PatchSkeletalMesh smesh1, PatchSkeletalMesh smesh2, PatchSkeleton skl, PatchSection section1, PatchSection section2, PatchSkeletonBone crossingBone) { if (smesh1 == null || smesh2 == null) { return; } // 各メッシュを大雑把にスケルトンに合わせる PatchSkeletonFitting.Fitting(smesh1, skl); PatchSkeletonFitting.Fitting(smesh2, skl); #if _DEBUG PatchSkeletalMeshRenderer.ToBitmap(smesh1, new List <PatchSection>() { section1 }).Save("output/5_1_mesh1_fitting.png"); PatchSkeletalMeshRenderer.ToBitmap(smesh2, new List <PatchSection>() { section2 }).Save("output/5_2_mesh2_fitting.png"); #endif // サイズの修正は手動でやる // 回転はFitting()でやってるから必要ない // 位置の調整 AdjustPosition(smesh1, smesh2, skl, section1, section2, crossingBone); #if _DEBUG PatchSkeletalMeshRenderer.ToBitmap(smesh1, new List <PatchSection>() { section1 }).Save("output/5_3_mesh1_AdjustPosition.png"); PatchSkeletalMeshRenderer.ToBitmap(smesh2, new List <PatchSection>() { section2 }).Save("output/5_4_mesh2_AdjustPosition.png"); #endif // メッシュを伸ばして繋げる Expand(smesh1, smesh2, skl, section1, section2, crossingBone); #if _DEBUG PatchSkeletalMeshRenderer.ToBitmap(smesh1, new List <PatchSection>() { section1 }).Save("output/5_5_mesh1_ExpandPatches.png"); PatchSkeletalMeshRenderer.ToBitmap(smesh2, new List <PatchSection>() { section2 }).Save("output/5_6_mesh2_ExpandPatches.png"); #endif }
private static List <int> CombinePath(List <int> path1, List <int> path2, List <PatchVertex> vertices1, List <PatchVertex> vertices2, PatchSection section1, PatchSection section2, Dictionary <PointF, int> p2i) { List <int> path = new List <int>(); HashSet <int> sectionIndices1 = new HashSet <int>(); for (int i = section1.First; i < section1.First + section1.Length; i++) { sectionIndices1.Add(i); } HashSet <int> sectionIndices2 = new HashSet <int>(); for (int i = section2.First; i < section2.First + section2.Length; i++) { sectionIndices2.Add(i); } HashSet <PointF> pathPoints = new HashSet <PointF>(); // 切り口の端からパスを登録していく. 切り口の両端は必ずpathに含まれている。と思う for (int _i = section1.First + section1.Length; _i != section1.First + 1; _i = FMath.Rem(_i + 1, path1.Count)) { int i = path1[_i]; if (sectionIndices1.Contains(i)) { continue; } path.Add(p2i[vertices1[i].position]); pathPoints.Add(vertices1[i].position); } PointF path1_preendPt = vertices1[path1[FMath.Rem(section1.First - 1, path1.Count)]].position; PointF path1_endPt = vertices1[path1[FMath.Rem(section1.First, path1.Count)]].position; int path2_start = -1; for (int i = 0; i < path2.Count; i++) { if (vertices2[path2[i]].position == path1_endPt) { path2_start = i; break; } } if (path2_start < 0) { return(new List <int>()); } int path2_poststart = FMath.Rem(path2_start + 1, path2.Count); PointF path2_poststartPt = vertices2[path2[path2_poststart]].position; int dir = 1; if (path2_poststartPt == path1_preendPt) { dir = -1; } for (int i = path2_start + dir; ; i += dir) { PointF pt = vertices2[path2[FMath.Rem(i, path2.Count)]].position; if (path.Contains(p2i[pt])) { break; } path.Add(p2i[pt]); } return(path); }
public void StringFormat () { IntPtr sf; Assert.AreEqual (Status.Ok, GDIPlus.GdipCreateStringFormat (Int32.MinValue, Int32.MinValue, out sf), "GdipCreateStringFormat"); CheckStringFormat (sf, (StringFormatFlags) Int32.MinValue, StringTrimming.Character); CharacterRange[] ranges = new CharacterRange[32]; Assert.AreEqual (Status.InvalidParameter, GDIPlus.GdipSetStringFormatMeasurableCharacterRanges (IntPtr.Zero, 1, ranges), "GdipSetStringFormatMeasurableCharacterRanges-null"); Assert.AreEqual (Status.InvalidParameter, GDIPlus.GdipSetStringFormatMeasurableCharacterRanges (IntPtr.Zero, -1, ranges), "GdipSetStringFormatMeasurableCharacterRanges-negative"); Assert.AreEqual (Status.Ok, GDIPlus.GdipSetStringFormatMeasurableCharacterRanges (sf, 1, ranges), "GdipSetStringFormatMeasurableCharacterRanges"); Assert.AreEqual (Status.Ok, GDIPlus.GdipSetStringFormatMeasurableCharacterRanges (sf, 32, ranges), "GdipSetStringFormatMeasurableCharacterRanges-32"); Assert.AreEqual (Status.ValueOverflow, GDIPlus.GdipSetStringFormatMeasurableCharacterRanges (sf, 33, ranges), "GdipSetStringFormatMeasurableCharacterRanges-33"); float first = Single.MinValue; float[] tabs = new float[1]; Assert.AreEqual (Status.InvalidParameter, GDIPlus.GdipSetStringFormatTabStops (IntPtr.Zero, 1.0f, 1, tabs), "GdipSetStringFormatTabStops-null"); Assert.AreEqual (Status.InvalidParameter, GDIPlus.GdipSetStringFormatTabStops (sf, 1.0f, 1, null), "GdipSetStringFormatTabStops-null/tabs"); Assert.AreEqual (Status.Ok, GDIPlus.GdipSetStringFormatTabStops (sf, 1.0f, -1, tabs), "GdipSetStringFormatTabStops-negative"); Assert.AreEqual (Status.Ok, GDIPlus.GdipGetStringFormatTabStops (sf, 1, out first, tabs), "GdipGetStringFormatTabStops-negative"); Assert.AreEqual (0.0f, first, "first-negative"); Assert.AreEqual (Status.Ok, GDIPlus.GdipSetStringFormatTabStops (sf, 1.0f, 1, tabs), "GdipSetStringFormatTabStops"); Assert.AreEqual (Status.Ok, GDIPlus.GdipGetStringFormatTabStops (sf, 1, out first, tabs), "GdipGetStringFormatTabStops"); Assert.AreEqual (1.0f, first, "first"); Assert.AreEqual (Status.InvalidParameter, GDIPlus.GdipDeleteStringFormat (IntPtr.Zero), "GdipDeleteStringFormat-null"); Assert.AreEqual (Status.Ok, GDIPlus.GdipDeleteStringFormat (sf), "GdipDeleteStringFormat"); }
//-------------------------------------------------------------------------- // Combine() //-------------------------------------------------------------------------- /// <summary> /// 2つのメッシュを統合して1つのARAP可能なメッシュを作成する /// TODO: さすがに関数にわけるべき /// </summary> static PatchSkeletalMesh Combine(PatchSkeletalMesh smesh1, PatchSkeletalMesh smesh2, PatchSection section1, PatchSection section2) { // // Meshを作成 // List <PatchVertex> vertices = CombineVertices(smesh1.mesh.vertices, smesh2.mesh.vertices); // 頂点から各種インデックスへの辞書を作っておく Dictionary <PointF, int> p2i = new Dictionary <PointF, int>(); for (int i = 0; i < vertices.Count; i++) { p2i[vertices[i].position] = i; } Dictionary <PointF, int> pt2part = vertices.ToDictionary(v => v.position, v => v.part); Dictionary <int, int> part2part_1 = new Dictionary <int, int>(); // smesh1の各パートが新しいメッシュのどのパートになるか foreach (var v in smesh1.mesh.vertices) { part2part_1[v.part] = pt2part[v.position]; } Dictionary <int, int> part2part_2 = new Dictionary <int, int>(); // smesh2の各パートが新しいメッシュのどのパートになるか foreach (var v in smesh2.mesh.vertices) { part2part_2[v.part] = pt2part[v.position]; } List <PatchControlPoint> controlPoints = CombineControlPoints(smesh1.mesh.CopyControlPoints(), smesh2.mesh.CopyControlPoints(), part2part_1, part2part_2); List <PatchTriangle> triangles = CombineTriangles(smesh1.mesh.triangles, smesh2.mesh.triangles, smesh1.mesh.vertices, smesh2.mesh.vertices, p2i); List <int> path = CombinePath(smesh1.mesh.pathIndices, smesh2.mesh.pathIndices, smesh1.mesh.vertices, smesh2.mesh.vertices, section1, section2, p2i); PatchMesh rawMesh = new PatchMesh(vertices, controlPoints, triangles, path); // // 骨格を統合 // PatchSkeleton skl = CombineSkeleton(smesh1.skl, smesh2.skl); // // 切り口を統合. // List <PatchSection> sections = CombineSections( smesh1.sections, smesh2.sections, section1, section2, smesh1.mesh.vertices, smesh2.mesh.vertices, smesh1.Mesh.pathIndices, smesh2.Mesh.pathIndices, path, p2i); // // SkeletalMeshを作成. // PatchSkeletalMesh newMesh = new PatchSkeletalMesh(rawMesh, skl, sections); return(newMesh); }
// 切り口に隣接する部分パス(2つ)を返す // この部分を引き伸ばしてメッシュを繋げる static Tuple <CharacterRange, CharacterRange> SectionToAdjuscentCurves(List <PointF> path, PatchSection section, int maxPtNum, float maxAngle) { if (section.Length <= 0 || path == null || path.Count < 5 || maxPtNum < 3) { return(null); } float cos = (float)Math.Cos(maxAngle); // 1つ目の部分パス int start1 = section.First; while (start1 < 0) { start1 += path.Count; } start1 = start1 % path.Count; int end1 = start1 - 2; for (int i = 0; i < maxPtNum - 2; i++) { int idx = (start1 - 2 - i + path.Count) % path.Count; var curve = new List <PointF>(); for (int j = idx; j < idx + 3; j++) { curve.Add(path[FMath.Rem(j, path.Count)]); } if (FMath.GetAngleCos(curve) < cos) { break; } end1 = idx; } CharacterRange r1 = new CharacterRange(); if (start1 > end1) { r1 = new CharacterRange(end1, start1 - end1 + 1); } if (start1 < end1) { // 始点と終点をまたがっている場合 r1 = new CharacterRange(end1, start1 + path.Count - end1 + 1); } if (r1.Length <= 0) { return(null); } // 2つ目の部分パス int start2 = section.First + section.Length - 1; while (start2 < 0) { start2 += path.Count; } start2 = start2 % path.Count; int end2 = start2 + 2; for (int i = 0; i < maxPtNum - 2; i++) { int idx = (start2 + i) % path.Count; var curve = path.Skip(idx).Take(3).ToList(); if (FMath.GetAngleCos(curve) < cos) { break; } end2 = start2 + i + 2; } CharacterRange r2 = new CharacterRange(); if (start2 < end2) { r2 = new CharacterRange(start2, end2 - start2 + 1); } if (start2 > end2) { r2 = new CharacterRange(start2, end2 + path.Count - start2 + 1); } if (r2.Length <= 0) { return(null); } return(new Tuple <CharacterRange, CharacterRange>(r1, r2)); }
// // Expand() // /// <summary> /// smesh1, smesh2の輪郭をずらして重ねる。輪郭に制御点をおいてARAPする /// </summary> static void Expand(PatchSkeletalMesh smesh1, PatchSkeletalMesh smesh2, PatchSkeleton skl, PatchSection section1, PatchSection section2, PatchSkeletonBone bone) { List <PatchVertex> rawPath1 = GetPath(smesh1); List <PatchVertex> rawPath2 = GetPath(smesh2); List <PointF> path1 = rawPath1.Select(v => v.position).ToList(); List <PointF> path2 = rawPath2.Select(v => v.position).ToList(); // // 輪郭を変形できるように制御点を作り直す // // smesh1 smesh1.mesh.ClearControlPoints(); foreach (var v in rawPath1) { smesh1.mesh.AddControlPoint(v.position, v.orgPosition); } smesh1.mesh.BeginDeformation(); // smesh2 smesh2.mesh.ClearControlPoints(); foreach (var v in rawPath2) { smesh2.mesh.AddControlPoint(v.position, v.orgPosition); } smesh2.mesh.BeginDeformation(); // // 切り口に隣接する2曲線を各切り口について取得し、これらが重なるように輪郭をずらす // // 切り口に隣接する2曲線をそれぞれ取得 var rawCurves1 = SectionToAdjuscentCurves(path1, section1, 5, 30); var rawCurves2 = SectionToAdjuscentCurves(path2, section2, 5, 30); if (rawCurves1 == null || rawCurves2 == null) { return; } PatchSkeletonBone refBone = null; foreach (var b in skl.bones) { if (bone == b) { refBone = b; break; } } // curves1, curves2の第一要素、第二要素がそれぞれ向かい合う(ボーンにとって同じ側の)切り口となるように並び替える var curves1 = GetSortedCurves(path1, rawCurves1, refBone); var curves2 = GetSortedCurves(path2, rawCurves2, refBone); if (curves1.Count != 2 || curves2.Count != 2) { return; } // curves1, curves2の移動履歴を記録 List <Tuple <PointF, PointF> > move1 = new List <Tuple <PointF, PointF> >(); List <Tuple <PointF, PointF> > move2 = new List <Tuple <PointF, PointF> >(); // 対応する曲線間で2点がかぶる(同じ座標になる)ように変形。 for (int i = 0; i < 2; i++) { var p1 = curves1[i].First(); var v1 = new PointF(p1.X - curves1[i].Last().X, p1.Y - curves1[i].Last().Y); var p2 = curves2[i].First(); var v2 = new PointF(curves2[i].Last().X - p2.X, curves2[i].Last().Y - p2.Y); // 2点かぶらせる int cnt = curves1[i].Count + curves2[i].Count - 2; if (cnt <= 1) { continue; } for (int j = 0; j < curves1[i].Count; j++) { PointF to = FMath.HelmitteInterporate(p1, v1, p2, v2, (float)j / (cnt - 1)); if (j == curves1[i].Count - 1) { move1.Add(new Tuple <PointF, PointF>(curves1[i][j], to)); } smesh1.mesh.TranslateControlPoint(curves1[i][j], to, false); } for (int j = 0; j < curves2[i].Count; j++) { PointF to = FMath.HelmitteInterporate(p1, v1, p2, v2, (float)(-j + cnt - 1) / (cnt - 1)); if (j == curves2[i].Count - 1) { move2.Add(new Tuple <PointF, PointF>(curves2[i][j], to)); } smesh2.mesh.TranslateControlPoint(curves2[i][j], to, false); } } // // 各曲線の動きに合わせて切り口を動かす // List <PointF> sections1 = new List <PointF>(); for (int i = section1.First + 1; i < section1.First + section1.Length - 1; i++) { sections1.Add(path1[FMath.Rem(i, path1.Count)]); } List <PointF> newSection1 = ARAPDeformation.ARAPDeformation.Deform(sections1, move1); if (newSection1.Count == sections1.Count) { for (int i = 0; i < newSection1.Count; i++) { smesh1.mesh.TranslateControlPoint(sections1[i], newSection1[i], false); } } List <PointF> sections2 = new List <PointF>(); for (int i = section2.First + 1; i < section2.First + section2.Length - 1; i++) { sections2.Add(path2[FMath.Rem(i, path2.Count)]); } List <PointF> newSection2 = ARAPDeformation.ARAPDeformation.Deform(sections2, move2); if (newSection2.Count == sections2.Count) { for (int i = 0; i < newSection2.Count; i++) { smesh2.mesh.TranslateControlPoint(sections2[i], newSection2[i], false); } } // // 変形 // smesh1.mesh.FlushDefomation(); smesh2.mesh.FlushDefomation(); // // 変形終了 // smesh1.mesh.EndDeformation(); smesh2.mesh.EndDeformation(); }
// ボーンのdst方向にあるのがpatch2 static void OverlayPatches(PatchSkeletalMesh patch1, PatchSkeletalMesh patch2, PatchSkeleton refSkeleton, PatchSection section1, PatchSection section2, PatchSkeletonBone bone, float border) { PatchSkeletonBone refBone = RefBone(refSkeleton, bone); float min1 = float.MaxValue; float max2 = float.MinValue; List <PatchVertex> path1 = GetPath(patch1); List <PatchVertex> path2 = GetPath(patch2); for (int i = section1.First; i < section1.First + section1.Length; i++) { var pt = path1[FMath.Rem(i, path1.Count)].position; float param = FMath.ParameterOnLine(pt, refBone.src.position, refBone.dst.position); if (float.IsNaN(param)) { continue; } min1 = Math.Min(min1, param); } for (int i = section2.First; i < section2.First + section2.Length; i++) { var pt = path2[FMath.Rem(i, path2.Count)].position; float param = FMath.ParameterOnLine(pt, refBone.src.position, refBone.dst.position); if (float.IsNaN(param)) { continue; } max2 = Math.Max(max2, param); } PointF x, y; BoneCoordinate(refBone, out x, out y); float boneLength = FMath.Distance(refBone.src.position, refBone.dst.position); float dparam = border / boneLength; float delta = (min1 - max2 - dparam) * boneLength; float dx = x.X * delta; float dy = x.Y * delta; // mesh2の頂点・制御点を平行移動する foreach (var v in patch2.mesh.vertices) { v.position = new PointF(v.position.X + dx, v.position.Y + dy); } foreach (var c in patch2.mesh.CopyControlPoints()) { patch2.mesh.TranslateControlPoint(c.position, new PointF(c.position.X + dx, c.position.Y + dy), false); } }
// // Expand() // /// <summary> /// smesh1, smesh2の輪郭をずらして重ねる。輪郭に制御点をおいてARAPする /// curveOffset, curveLengthは切り口付近の補間に使う部分曲線 /// </summary> static void Expand( PatchSkeletalMesh patch1, PatchSkeletalMesh patch2, PatchSkeleton refSkeleton, PatchSection section1, PatchSection section2, PatchSkeletonBone bone, float curveLength, float curveBuffer) { // メッシュを固定 patch1.mesh.FreezeMesh(true); patch2.mesh.FreezeMesh(true); List <PatchVertex> rawPath1 = GetPath(patch1); List <PatchVertex> rawPath2 = GetPath(patch2); List <PointF> path1 = rawPath1.Select(v => v.position).ToList(); List <PointF> path2 = rawPath2.Select(v => v.position).ToList(); // 切り口に隣接する2曲線をそれぞれ取得 var rawCurves1 = section2adjCurves(rawPath1, section1); var rawCurves2 = section2adjCurves(rawPath2, section2); if (rawCurves1 == null || rawCurves2 == null) { return; } var trimCurves1 = TrimCurves(rawPath1, rawCurves1, 0, curveLength); var trimCurves2 = TrimCurves(rawPath2, rawCurves2, 0, curveLength); PatchSkeletonBone refBone = RefBone(refSkeleton, bone); var sorted1 = GetSortedCurves(path1, trimCurves1, refBone); var sorted2 = GetSortedCurves(path2, trimCurves2, refBone); if (sorted1.Count != 2 || sorted2.Count != 2) { return; } // patch1の制御点 patch1.mesh.ClearControlPoints(); for (int i = 0; i < 2; i++) { foreach (var p in sorted1[i]) { patch1.mesh.AddControlPoint(p, p); } } patch1.mesh.BeginDeformation(); // patch2の制御点 patch2.mesh.ClearControlPoints(); for (int i = 0; i < 2; i++) { foreach (var p in sorted2[i]) { patch2.mesh.AddControlPoint(p, p); } } patch2.mesh.BeginDeformation(); // 第一要素、第二要素がそれぞれボーンにとって同じ側の切り口となるように並び替える // 切り口に近づく向きに点が並んでいる float h10 = CurveHeight(sorted1[0], refBone); float h11 = CurveHeight(sorted1[1], refBone); float h20 = CurveHeight(sorted2[0], refBone); float h21 = CurveHeight(sorted2[1], refBone); var curve10 = sorted1[0].ToList(); var curve11 = sorted1[1].ToList(); var curve20 = sorted2[0].ToList(); var curve21 = sorted2[1].ToList(); // curveの重心を揃える PointF x, y; BoneCoordinate(refBone, out x, out y); float c0 = (h10 + h20) * 0.5f; float dy10 = c0 - h10; for (int i = 0; i < curve10.Count; i++) { curve10[i] = new PointF(curve10[i].X + dy10 * y.X, curve10[i].Y + dy10 * y.Y); } float dy11 = c0 - h20; for (int i = 0; i < curve20.Count; i++) { curve20[i] = new PointF(curve20[i].X + dy11 * y.X, curve20[i].Y + dy11 * y.Y); } float c1 = (h11 + h21) * 0.5f; float dy20 = c1 - h11; for (int i = 0; i < curve11.Count; i++) { curve11[i] = new PointF(curve11[i].X + dy20 * y.X, curve11[i].Y + dy20 * y.Y); } float dy21 = c1 - h21; for (int i = 0; i < curve21.Count; i++) { curve21[i] = new PointF(curve21[i].X + dy21 * y.X, curve21[i].Y + dy21 * y.Y); } // curveをエルミート保管して繋げる var ends0 = new[] { curve10[0], curve10.Last(), curve20[0], curve20.Last() }; int min0, max0; FMath.GetMinElement(ends0, p => FMath.ParameterOnLine(p, refBone.src.position, refBone.dst.position), out min0); FMath.GetMinElement(ends0, p => - FMath.ParameterOnLine(p, refBone.src.position, refBone.dst.position), out max0); var ends1 = new[] { curve11[0], curve11.Last(), curve21[0], curve21.Last() }; int min1, max1; FMath.GetMinElement(ends1, p => FMath.ParameterOnLine(p, refBone.src.position, refBone.dst.position), out min1); FMath.GetMinElement(ends1, p => - FMath.ParameterOnLine(p, refBone.src.position, refBone.dst.position), out max1); PointF[] es = new[] { ends0[min0], ends1[min1], ends0[max0], ends1[max1], }; PointF[] vs = new[] { new PointF(curve10[0].X - curve10[curve10.Count - 1].X, curve10[0].Y - curve10[curve10.Count - 1].Y), new PointF(curve11[0].X - curve11[curve11.Count - 1].X, curve11[0].Y - curve11[curve11.Count - 1].Y), new PointF(-(curve20[0].X - curve20[curve20.Count - 1].X), -(curve20[0].Y - curve20[curve20.Count - 1].Y)), new PointF(-(curve21[0].X - curve21[curve21.Count - 1].X), -(curve21[0].Y - curve21[curve21.Count - 1].Y)), }; float[] ps = new[] { FMath.ParameterOnLine(es[0], refBone.src.position, refBone.dst.position), FMath.ParameterOnLine(es[1], refBone.src.position, refBone.dst.position), FMath.ParameterOnLine(es[2], refBone.src.position, refBone.dst.position), FMath.ParameterOnLine(es[3], refBone.src.position, refBone.dst.position), }; for (int i = 0; i < 2; i++) { var e1 = es[i]; var v1 = vs[i]; float param1 = ps[i]; var e2 = es[i + 2]; var v2 = vs[i + 2]; float param2 = ps[i + 2]; if (Math.Abs(param2 - param1) <= 1e-4) { continue; } float paramRatio = 1 / (param2 - param1); for (int j = 0; j < sorted1[i].Count; j++) { float param = FMath.ParameterOnLine(sorted1[i][j], refBone.src.position, refBone.dst.position); float t = (param - param1) * paramRatio; PointF to = FMath.HelmitteInterporate(e1, v1, e2, v2, t); patch1.mesh.TranslateControlPoint(sorted1[i][j], to, false); } for (int j = 0; j < sorted2[i].Count; j++) { float param = FMath.ParameterOnLine(sorted2[i][j], refBone.src.position, refBone.dst.position); float t = (param - param1) * paramRatio; PointF to = FMath.HelmitteInterporate(e1, v1, e2, v2, t); patch2.mesh.TranslateControlPoint(sorted2[i][j], to, false); } } // 変形 patch1.mesh.FlushDefomation(); patch2.mesh.FlushDefomation(); // 変形終了 patch1.mesh.EndDeformation(); patch2.mesh.EndDeformation(); }
public void SetMeasurableCharacterRanges(CharacterRange[] ranges) { int status = SafeNativeMethods.Gdip.GdipSetStringFormatMeasurableCharacterRanges(new HandleRef(this, this.nativeFormat), ranges.Length, ranges); if (status != 0) { throw SafeNativeMethods.Gdip.StatusException(status); } }
/// <summary> /// Calculate widthLimit/height of a text ( /// </summary> public static SizeF TextSize(Graphics g, string aText, Font aFont) { if (aText.Trim() == "") return new SizeF(0, 0); if (aText[aText.Length - 1] == ' ') { aText = aText.Substring(0, aText.Length - 1) + "/"; } #if CF return TextSize2(g, aText, aFont); #else try { if (aFont.Style == FontStyle.Bold) { return TextSize2(g, aText, aFont); } //MeasureCharacterRanges return wrong value if bold. StringFormat aFormat = StringFormat.GenericTypographic; //aFormat.Trimming = System.Drawing.StringTrimming.Character; //aFormat.FormatFlags += 16384; //aFormat.FormatFlags = aFormat.FormatFlags & StringFormatFlags.NoClip; CharacterRange[] cr = new CharacterRange[1]; cr[0] = new CharacterRange(0, aText.Length); aFormat.SetMeasurableCharacterRanges(cr); RectangleF aRect = Screen.PrimaryScreen.Bounds; aRect = g.MeasureCharacterRanges(aText, aFont, aRect, aFormat)[0].GetBounds(g); return new SizeF(aRect.Right - aRect.Left, aRect.Height); } catch { return TextSize2(g, aText, aFont); } #endif }
private void PaintException(PaintEventArgs e, Exception ex) { StringFormat stringFormat = new StringFormat { Alignment = StringAlignment.Near, LineAlignment = StringAlignment.Near }; string text = ex.ToString(); CharacterRange[] ranges = new CharacterRange[] { new CharacterRange(0, text.Length) }; stringFormat.SetMeasurableCharacterRanges(ranges); int num = 2; Size iconSize = SystemInformation.IconSize; int x = num * 2; int y = num * 2; Rectangle clientRectangle = this.Control.ClientRectangle; Rectangle rect = clientRectangle; rect.X++; rect.Y++; rect.Width -= 2; rect.Height -= 2; Rectangle rectangle3 = new Rectangle(x, y, iconSize.Width, iconSize.Height); Rectangle layoutRect = clientRectangle; layoutRect.X = (rectangle3.X + rectangle3.Width) + (2 * x); layoutRect.Y = rectangle3.Y; layoutRect.Width -= (layoutRect.X + x) + num; layoutRect.Height -= (layoutRect.Y + y) + num; using (Font font = new Font(this.Control.Font.FontFamily, (float) Math.Max((SystemInformation.ToolWindowCaptionHeight - SystemInformation.BorderSize.Height) - 2, this.Control.Font.Height), GraphicsUnit.Pixel)) { using (Region region = e.Graphics.MeasureCharacterRanges(text, font, layoutRect, stringFormat)[0]) { Region clip = e.Graphics.Clip; e.Graphics.ExcludeClip(region); e.Graphics.ExcludeClip(rectangle3); try { e.Graphics.FillRectangle(Brushes.White, clientRectangle); } finally { e.Graphics.Clip = clip; } using (Pen pen = new Pen(Color.Red, (float) num)) { e.Graphics.DrawRectangle(pen, rect); } Icon error = SystemIcons.Error; e.Graphics.FillRectangle(Brushes.White, rectangle3); e.Graphics.DrawIcon(error, rectangle3.X, rectangle3.Y); layoutRect.X++; e.Graphics.IntersectClip(region); try { e.Graphics.FillRectangle(Brushes.White, layoutRect); e.Graphics.DrawString(text, font, new SolidBrush(this.Control.ForeColor), layoutRect, stringFormat); } finally { e.Graphics.Clip = clip; } } } stringFormat.Dispose(); }
private StylizedRowElement[] combine(DocumentRow row, ICollection<FaceApplied> styles) { if (styles == null || styles.Count == 0) { var result = new StylizedRowElement[1]; result[0] = new StylizedRowElement(new CharacterRange(0, row.Length), style); return result; } var elements = new List<CharacterRange> { new CharacterRange(0, row.Length) }; foreach (var styleApplied in styles) { var faceRange = styleApplied.Range; for (var i = 0; i < elements.Count; ++i) { CharacterRange element = elements[i]; //if we are before range needed if (element == faceRange) { continue; } if (element.First + element.Length < faceRange.First) { continue; } // if we are after range needed if (element.First >= faceRange.First + faceRange.Length) { break; } // if face range after element start if (element.First < faceRange.First) { var preFace = new CharacterRange(element.First, faceRange.First - element.First); element.Length -= preFace.Length; element.First += preFace.Length; elements[i] = element; elements.Insert(i++, preFace); } if (element.First + element.Length <= faceRange.First + faceRange.Length) { continue; } var afterOffset = faceRange.First + faceRange.Length; var afterOffsetLength = element.First + element.Length - afterOffset; element.Length -= afterOffsetLength; elements[i] = element; elements.Insert(++i, new CharacterRange(afterOffset, afterOffsetLength)); } } var els = new List<StylizedRowElement>(); foreach (var range in elements) { var rangeStyle = Style; foreach (var face in styles) { if (!intersect(face.Range, range)) continue; rangeStyle = rangeStyle.Combine(FindFace(face.Face).FaceStyle); } els.Add(new StylizedRowElement(range, rangeStyle)); } return els.ToArray(); }
/// <devdoc> /// Calculate character ranges taking into account the locale. Provided for surrogate chars support. /// </devdoc> private CharacterRange[] AdjustCharacterRangesForSurrogateChars(){ string text = Text; if (String.IsNullOrEmpty(text)) { return new CharacterRange[]{}; } StringInfo stringInfo = new StringInfo(text); int textLen = stringInfo.LengthInTextElements; ArrayList ranges = new ArrayList(Links.Count); foreach (Link link in Links) { int charStart = ConvertToCharIndex(link.Start, text); int charEnd = ConvertToCharIndex(link.Start + link.Length, text); if (LinkInText(charStart, charEnd - charStart)) { int length = (int) Math.Min(link.Length, textLen - link.Start); ranges.Add(new CharacterRange(charStart, ConvertToCharIndex(link.Start + length, text) - charStart)); } } CharacterRange[] regions = new CharacterRange[ranges.Count + 1]; ranges.CopyTo(regions, 0); regions[regions.Length - 1] = new CharacterRange(0, text.Length); return regions; }
private static float OverlapWidth(PatchSkeletalMesh patch1, PatchSkeletalMesh patch2, PatchSkeleton refSkeleton, PatchSection section1, PatchSection section2, PatchSkeletonBone bone, int maxLength, int maxAngle) { var refBone = RefBone(refSkeleton, bone); var path1 = GetPath(patch1); var curves1 = section2adjCurves(path1, section1); var sorted1 = GetSortedCurves(path1.Select(v => v.position).ToList(), curves1, refBone); var path2 = GetPath(patch2); var curves2 = section2adjCurves(path2, section2); var sorted2 = GetSortedCurves(path2.Select(v => v.position).ToList(), curves2, refBone); float[] param = new float[] { 0, 0, 0, 0 }; var sortedCurves = new[] { sorted1[0], sorted1[1], sorted2[0], sorted2[1], }; float minCos = (float)Math.Cos(Math.PI / 180 * maxAngle); for (int i = 0; i < sortedCurves.Length; i++) { var c = sortedCurves[i]; var path = i < 2 ? path1 : path2; List <PointF> pts = new List <PointF>(); float total = 0; for (int j = c.Count - 1; j >= 0; j--) { var p = c[j]; pts.Add(p); if (pts.Count >= 3 && FMath.GetAngleCos(pts) < minCos) { pts.RemoveAt(pts.Count - 1); break; } if (pts.Count >= 2) { total += FMath.Distance(pts[pts.Count - 2], pts[pts.Count - 1]); } if (total >= maxLength) { pts.RemoveAt(pts.Count - 1); break; } } param[i] = FMath.ParameterOnLine(pts.Last(), refBone.src.position, refBone.dst.position); } float min1 = Enumerable.Range(section1.First, section1.Length).Min(idx => FMath.ParameterOnLine(path1[FMath.Rem(idx, path1.Count)].position, refBone.src.position, refBone.dst.position)); float max1 = Enumerable.Range(section1.First, section1.Length).Max(idx => FMath.ParameterOnLine(path1[FMath.Rem(idx, path1.Count)].position, refBone.src.position, refBone.dst.position)); int idx1 = Enumerable.Range(section1.First, section1.Length).First(idx => max1 == FMath.ParameterOnLine(path1[FMath.Rem(idx, path1.Count)].position, refBone.src.position, refBone.dst.position)); float min2 = Enumerable.Range(section2.First, section2.Length).Min(idx => FMath.ParameterOnLine(path2[FMath.Rem(idx, path2.Count)].position, refBone.src.position, refBone.dst.position)); float max2 = Enumerable.Range(section2.First, section2.Length).Max(idx => FMath.ParameterOnLine(path2[FMath.Rem(idx, path2.Count)].position, refBone.src.position, refBone.dst.position)); float idx2 = Enumerable.Range(section2.First, section2.Length).First(idx => max2 == FMath.ParameterOnLine(path2[FMath.Rem(idx, path2.Count)].position, refBone.src.position, refBone.dst.position)); System.Diagnostics.Debug.Assert(param[0] <= max1); System.Diagnostics.Debug.Assert(param[1] <= max1); System.Diagnostics.Debug.Assert(param[2] >= min2); System.Diagnostics.Debug.Assert(param[3] >= min2); float[] dParams = new[] { Math.Max(0, min1 - param[0]), Math.Max(0, min1 - param[1]), Math.Max(0, param[2] - max2), Math.Max(0, param[3] - max2), }; float minDParam = dParams.Min(); float boarderWidth = minDParam * FMath.Distance(refBone.src.position, refBone.dst.position); return(boarderWidth); }
public StylizedRowElement(CharacterRange range, Style style) { this.range = range; this.style = style; }
//----------------------------------------------------------------------------------- // Deform() //----------------------------------------------------------------------------------- /// <summary> /// 切り口付近で2つのsmeshが重なるように変形する /// patch2がdst方向 /// </summary> private static void Deform(PatchSkeletalMesh patch1, PatchSkeletalMesh patch2, PatchSkeleton refSkeleton, PatchSection section1, PatchSection section2, PatchSkeletonBone crossingBone) { if (patch1 == null || patch2 == null) { return; } // 各メッシュを大雑把にスケルトンに合わせる PatchSkeletonFitting.Fitting(patch1, refSkeleton); PatchSkeletonFitting.Fitting(patch2, refSkeleton); // TODO: サイズの修正 // 回転はFitting()でやってるから必要ない // 位置の調整 float overlap = OverlapWidth(patch1, patch2, refSkeleton, section1, section2, crossingBone, 100, 45); AdjustHeight(patch1, patch2, refSkeleton, section1, section2, crossingBone); OverlayPatches(patch1, patch2, refSkeleton, section1, section2, crossingBone, overlap); #if _DEBUG PatchSkeletalMeshRenderer.ToBitmap(patch1).Save("output_Connector2/4_patch1.png"); PatchSkeletalMeshRenderer.ToBitmap(patch2).Save("output_Connector2/4_patch2.png"); #endif // メッシュを伸ばして繋げる Expand(patch1, patch2, refSkeleton, section1, section2, crossingBone, overlap, overlap); }
private static bool intersect(CharacterRange lhs, CharacterRange rhs) { var min = Math.Max(lhs.First, rhs.First); var max = Math.Min(lhs.First + lhs.Length, rhs.First + rhs.Length); return min < max; }
/// <summary> /// Measures the location of an arbritrary # of words within a string /// </summary> private WordStartFinish[] MeasureString(string s, Graphics g, Font drawFont, StringFormat drawFormat, CharacterRange[] cra) { if (cra.Length <= MEASUREMAX) // handle the simple case of < MEASUREMAX words return MeasureString32(s, g, drawFont, drawFormat, cra); // Need to compensate for SetMeasurableCharacterRanges limitation of 32 (MEASUREMAX) int mcra = (cra.Length / MEASUREMAX); // # of full 32 arrays we need int ip = cra.Length % MEASUREMAX; // # of partial entries needed for last array (if any) WordStartFinish[] sz = new WordStartFinish[cra.Length]; // this is the final result; float startPos=0; CharacterRange[] cra32 = new CharacterRange[MEASUREMAX]; // fill out int icra=0; // index thru the cra for (int i=0; i < mcra; i++) { // fill out the new array int ticra = icra; for (int j=0; j < cra32.Length; j++) { cra32[j] = cra[ticra++]; cra32[j].First -= cra[icra].First; // adjust relative offsets of strings } // measure the word locations (in the new string) // ???? should I put a blank in front of it?? string ts = s.Substring(cra[icra].First, cra[icra + cra32.Length-1].First + cra[icra + cra32.Length-1].Length - cra[icra].First); WordStartFinish[] pos = MeasureString32(ts, g, drawFont, drawFormat, cra32); // copy the values adding in the new starting positions for (int j = 0; j < pos.Length; j++) { sz[icra].start = pos[j].start + startPos; sz[icra++].end = pos[j].end + startPos; } startPos = sz[icra-1].end; // reset the start position for the next line } // handle the remaining character if (ip > 0) { // resize the range array cra32 = new CharacterRange[ip]; // fill out the new array int ticra = icra; for (int j=0; j < cra32.Length; j++) { cra32[j] = cra[ticra++]; cra32[j].First -= cra[icra].First; // adjust relative offsets of strings } // measure the word locations (in the new string) // ???? should I put a blank in front of it?? string ts = s.Substring(cra[icra].First, cra[icra + cra32.Length-1].First + cra[icra + cra32.Length-1].Length - cra[icra].First); WordStartFinish[] pos = MeasureString32(ts, g, drawFont, drawFormat, cra32); // copy the values adding in the new starting positions for (int j = 0; j < pos.Length; j++) { sz[icra].start = pos[j].start + startPos; sz[icra++].end = pos[j].end + startPos; } } return sz; }
static PatchSkeletalMesh Combine(PatchSkeletalMesh smesh1, PatchSkeletalMesh smesh2, PatchSection section1, PatchSection section2) { throw new NotImplementedException(); }
private void RenderRowView( Graphics g, DocumentRowView view, float offset, CharacterRange selectionRange) { for (int i = 0; i < view.parts.Length; ++i) { StylizedRowElement atom = view.parts[i]; string atomRaw = view.partRaws[i]; CharacterRangeTrio trio = intersectAtomWithSelection(atom.range, selectionRange); if (trio.first.Length > 0) { trio.first.First -= atom.range.First; offset += DrawStringPart(trio.first, g, atomRaw, atom.style, offset).Width; } if (trio.second.Length > 0) { trio.second.First -= atom.range.First; Style thisStyle = atom.style.combine(selectionStyle); offset += DrawStringPart(trio.second, g, atomRaw, thisStyle, offset).Width; } if (trio.third.Length > 0) { trio.third.First -= atom.range.First; offset += DrawStringPart(trio.third, g, atomRaw, atom.style, offset).Width; } } renderStuff.LineFrame = offset; }
[Test] // adapted from bug #78777 public void MeasureCharacterRanges_TwoLines () { if (font == null) Assert.Ignore ("Couldn't create required font"); string text = "this\nis a test"; CharacterRange[] ranges = new CharacterRange[2]; ranges[0] = new CharacterRange (0, 5); ranges[1] = new CharacterRange (5, 9); StringFormat string_format = new StringFormat (); string_format.FormatFlags = StringFormatFlags.NoClip; string_format.SetMeasurableCharacterRanges (ranges); using (Bitmap bitmap = new Bitmap (20, 20)) { using (Graphics g = Graphics.FromImage (bitmap)) { SizeF size = g.MeasureString (text, font, new Point (0, 0), string_format); RectangleF layout_rect = new RectangleF (0.0f, 0.0f, size.Width, size.Height); Region[] regions = g.MeasureCharacterRanges (text, font, layout_rect, string_format); Assert.AreEqual (2, regions.Length, "Length"); Assert.AreEqual (regions[0].GetBounds (g).Height, regions[1].GetBounds (g).Height, "Height"); } } }
private void RenderRowViewWithCursor( Graphics g, DocumentRowView view, float offset, int cursor, CharacterRange selectionRange) { for (int i = 0, last = view.parts.Length - 1; i <= last; ++i) { StylizedRowElement atom = view.parts[i]; string atomRaw = view.partRaws[i]; if (i == last && cursor >= atom.range.Length) { cursor = atom.range.Length - 1; } CharacterRangeTrio trio = intersectAtomWithSelection(atom.range, selectionRange); if (trio.first.Length > 0) { trio.first.First -= atom.range.First; Style thisStyle = atom.style; float offsetChange = DrawStringPart(trio.first, g, atomRaw, thisStyle, offset).Width; cursor -= drawCursor(cursor, trio.first, offset, g, atomRaw, thisStyle); offset += offsetChange; } if (trio.second.Length > 0) { trio.second.First -= atom.range.First; Style thisStyle = atom.style.combine(selectionStyle); float offsetChange = DrawStringPart(trio.second, g, atomRaw, thisStyle, offset).Width; cursor -= drawCursor(cursor, trio.second, offset, g, atomRaw, thisStyle); offset += offsetChange; } if (trio.third.Length > 0) { trio.third.First -= atom.range.First; Style thisStyle = atom.style; float offsetChange = DrawStringPart(trio.third, g, atomRaw, atom.style, offset).Width; cursor -= drawCursor(cursor, trio.third, offset, g, atomRaw, thisStyle); offset += offsetChange; } } renderStuff.LineFrame = offset; }
public void MeasureCharacterRanges_Prefix () { if (font == null) Assert.Ignore ("Couldn't create required font"); string text = "Hello &Mono::"; CharacterRange[] ranges = new CharacterRange[1]; ranges[0] = new CharacterRange (5, 4); StringFormat string_format = new StringFormat (); string_format.SetMeasurableCharacterRanges (ranges); using (Bitmap bitmap = new Bitmap (20, 20)) { using (Graphics g = Graphics.FromImage (bitmap)) { SizeF size = g.MeasureString (text, font, new Point (0, 0), string_format); RectangleF layout_rect = new RectangleF (0.0f, 0.0f, size.Width, size.Height); // here & is part of the measure and visible string_format.HotkeyPrefix = HotkeyPrefix.None; Region[] regions = g.MeasureCharacterRanges (text, font, layout_rect, string_format); RectangleF bounds_none = regions[0].GetBounds (g); // here & is part of the measure (range) but visible as an underline string_format.HotkeyPrefix = HotkeyPrefix.Show; regions = g.MeasureCharacterRanges (text, font, layout_rect, string_format); RectangleF bounds_show = regions[0].GetBounds (g); Assert.IsTrue (bounds_show.Width < bounds_none.Width, "Show<None"); // here & is part of the measure (range) but invisible string_format.HotkeyPrefix = HotkeyPrefix.Hide; regions = g.MeasureCharacterRanges (text, font, layout_rect, string_format); RectangleF bounds_hide = regions[0].GetBounds (g); Assert.AreEqual (bounds_hide.Width, bounds_show.Width, "Hide==None"); } } }
private static CharacterRange intersect(CharacterRange range1, CharacterRange range2) { int start = Math.Max(range1.First, range2.First); int end = Math.Min(range1.First + range1.Length, range2.First + range2.Length); if (end > start) return new CharacterRange(start, end - start); return new CharacterRange(); }
private string[] MeasureString(PageText pt, Graphics g, out float[] width) { StyleInfo si = pt.SI; string s = pt.Text; Font drawFont=null; StringFormat drawFormat=null; SizeF ms; string[] sa=null; width=null; try { // STYLE System.Drawing.FontStyle fs = 0; if (si.FontStyle == FontStyleEnum.Italic) fs |= System.Drawing.FontStyle.Italic; // WEIGHT switch (si.FontWeight) { case FontWeightEnum.Bold: case FontWeightEnum.Bolder: case FontWeightEnum.W500: case FontWeightEnum.W600: case FontWeightEnum.W700: case FontWeightEnum.W800: case FontWeightEnum.W900: fs |= System.Drawing.FontStyle.Bold; break; default: break; } drawFont = new Font(StyleInfo.GetFontFamily(si.FontFamilyFull), si.FontSize, fs); drawFormat = new StringFormat(); drawFormat.Alignment = StringAlignment.Near; // Measure string // pt.NoClip indicates that this was generated by PageTextHtml Build. It has already word wrapped. if (pt.NoClip || pt.SI.WritingMode == WritingModeEnum.tb_rl) // TODO: support multiple lines for vertical text { ms = MeasureString(s, g, drawFont, drawFormat); width = new float[1]; width[0] = RSize.PointsFromPixels(g, ms.Width); // convert to points from pixels sa = new string[1]; sa[0] = s; return sa; } // handle multiple lines; // 1) split the string into the forced line breaks (ie "\n and \r") // 2) foreach of the forced line breaks; break these into words and recombine s = s.Replace("\r\n", "\n"); // don't want this to result in double lines string[] flines = s.Split(lineBreak); List<string> lines = new List<string>(); List<float> lineWidths = new List<float>(); // remove the size reserved for left and right padding float ptWidth = pt.W - pt.SI.PaddingLeft - pt.SI.PaddingRight; if (ptWidth <= 0) ptWidth = 1; foreach (string tfl in flines) { string fl; if (tfl.Length > 0 && tfl[tfl.Length-1] == ' ') fl = tfl.TrimEnd(' '); else fl = tfl; // Check if entire string fits into a line ms = MeasureString(fl, g, drawFont, drawFormat); float tw = RSize.PointsFromPixels(g, ms.Width); if (tw <= ptWidth) { // line fits don't need to break it down further lines.Add(fl); lineWidths.Add(tw); continue; } // Line too long; need to break into multiple lines // 1) break line into parts; then build up again keeping track of word positions string[] parts = fl.Split(wordBreak); // this is the maximum split of lines StringBuilder sb = new StringBuilder(fl.Length); CharacterRange[] cra = new CharacterRange[parts.Length]; for (int i = 0; i < parts.Length; i++) { int sc = sb.Length; // starting character sb.Append(parts[i]); // endding character if (i != parts.Length - 1) // last item doesn't need blank sb.Append(" "); int ec = sb.Length; CharacterRange cr = new CharacterRange(sc, ec - sc); cra[i] = cr; // add to character array } // 2) Measure the word locations within the line string wfl = sb.ToString(); WordStartFinish[] wordLocations = MeasureString(wfl, g, drawFont, drawFormat, cra); if (wordLocations == null) continue; // 3) Loop thru creating new lines as needed int startLoc = 0; CharacterRange crs = cra[startLoc]; CharacterRange cre = cra[startLoc]; float cwidth = wordLocations[0].end; // length of the first float bwidth = wordLocations[0].start; // characters need a little extra on start string ts; bool bLine = true; for (int i=1; i < cra.Length; i++) { cwidth = wordLocations[i].end - wordLocations[startLoc].start + bwidth; if (cwidth > ptWidth) { // time for a new line cre = cra[i-1]; ts = wfl.Substring(crs.First, cre.First + cre.Length - crs.First); lines.Add(ts); lineWidths.Add(wordLocations[i-1].end - wordLocations[startLoc].start + bwidth); // Find the first non-blank character of the next line while (i < cra.Length && cra[i].Length == 1 && fl[cra[i].First] == ' ') { i++; } if (i < cra.Length) // any lines left? { // yes, continue on startLoc = i; crs = cre = cra[startLoc]; cwidth = wordLocations[i].end - wordLocations[startLoc].start + bwidth; } else // no, we can stop bLine = false; // bwidth = wordLocations[startLoc].start - wordLocations[startLoc - 1].end; } else cre = cra[i]; } if (bLine) { ts = fl.Substring(crs.First, cre.First + cre.Length - crs.First); lines.Add(ts); lineWidths.Add(cwidth); } } // create the final array from the Lists string[] la = lines.ToArray(); width = lineWidths.ToArray(); return la; } finally { if (drawFont != null) drawFont.Dispose(); if (drawFormat != null) drawFont.Dispose(); } }
// 切り口に隣接する部分パス(2つ)を返す // この部分を引き伸ばしてメッシュを繋げる static Tuple <CharacterRange, CharacterRange> section2adjCurves(List <PatchVertex> path, PatchSection section) { if (section.Length <= 0 || path == null || path.Count < 5) { return(null); } int start1 = FMath.Rem(section.First, path.Count); int end1 = start1 - 2; int start2 = FMath.Rem(section.First + section.Length - 1, path.Count); int end2 = start2 + 2; int part = path[start1].part; System.Diagnostics.Debug.Assert(part == path[start2].part); for (int i = 0; i < path.Count / 2; i++) { int idx1 = FMath.Rem(start1 - i, path.Count); int idx2 = FMath.Rem(start2 + i, path.Count); if (path[idx1].part != part || path[idx2].part != part) { break; } end1 = idx1; end2 = idx2; if (FMath.Rem(end2 - end1, path.Count) <= 2) { break; } } var r1 = rangeOnLoop(end1, start1, path.Count); var r2 = rangeOnLoop(start2, end2, path.Count); if (r1.Length <= 0 || r2.Length <= 0) { return(null); } return(new Tuple <CharacterRange, CharacterRange>(r1, r2)); }
/// <summary> /// Measures the location of words within a string; limited by .Net 1.1 to 32 words /// MEASUREMAX is a constant that defines that limit /// </summary> /// <param name="s"></param> /// <param name="g"></param> /// <param name="drawFont"></param> /// <param name="drawFormat"></param> /// <param name="cra"></param> /// <returns></returns> private WordStartFinish[] MeasureString32(string s, Graphics g, Font drawFont, StringFormat drawFormat, CharacterRange[] cra) { if (s == null || s.Length == 0) return null; drawFormat.SetMeasurableCharacterRanges(cra); Region[] rs = new Region[cra.Length]; rs = g.MeasureCharacterRanges(s, drawFont, new RectangleF(0, 0, float.MaxValue, float.MaxValue), drawFormat); WordStartFinish[] sz = new WordStartFinish[cra.Length]; int isz = 0; foreach (Region r in rs) { RectangleF mr = r.GetBounds(g); sz[isz].start = RSize.PointsFromPixels(g, mr.Left); sz[isz].end = RSize.PointsFromPixels(g, mr.Right); isz++; } return sz; }
private RectangleF[] MeasureStringParts(CharacterRange[] range, Graphics g, string str, Style style) { UpdateTabStop(str, style); stringFormat.SetMeasurableCharacterRanges(range); Region[] regn = g.MeasureCharacterRanges(str, FontCache.get(style), RectangleF.Empty, stringFormat); return Array.ConvertAll<Region, RectangleF>(regn, delegate(Region rgn) { return rgn.GetBounds(g); }); }