├── .gitattributes
├── .github
└── workflows
│ └── dotnet-build.yml
├── .gitignore
├── CHANGELOG.md
├── Directory.Build.props
├── GeometryToolDemo.sln
├── LICENSE
├── README.md
├── build
└── Version.props
├── docs
└── image
│ └── README
│ ├── README0.png
│ ├── README1.png
│ ├── README2.png
│ ├── README3.png
│ ├── README4.png
│ └── README5.png
└── src
└── GeometryTool
└── GeometryTool
├── Action
├── BorderLock.cs
├── GeomortyStringConverter.cs
├── GraphAdd.cs
└── XMLHelper.cs
├── App.config
├── App.xaml
├── App.xaml.cs
├── Converter
├── BorderWidthHeightConverter.cs
├── CanvasScaleTransformConverter.cs
├── ImageVisibilityConverter.cs
├── IntBoolConverter.cs
├── ScaleCenterConverter.cs
└── SliderConverterToDouble.cs
├── GeometryTool.csproj
├── Image
├── Bezier.png
├── Circle.png
├── Color.png
├── Curve.png
├── Down.png
├── DowntoUp.png
├── Ellipse.png
├── Left.png
├── Line.png
├── Pan.png
├── QBezier.png
├── Rectangular.png
├── Right.png
├── RighttoLeft.png
├── Triangle.png
├── TurnOver.png
├── Up.png
├── UptoDown.png
├── XuanZhuan.png
├── cuxi.png
├── icon.png
├── lock.png
├── myCanvas.png
├── point.png
└── unlock.png
├── MainWindow.xaml
├── MainWindow.xaml.cs
├── Model
├── AutoPoints.cs
├── BorderWithAdorner.cs
├── BorderWithDrag.cs
├── GeometryAdorner.cs
├── GeometryChrome.cs
├── GraphAppearance.cs
├── LockAdorner.cs
├── LockChrome.cs
├── MoveThumb.cs
├── ResizeRotateAdorner.cs
├── ResizeRotateChrome.cs
├── ResizeThumb.cs
├── RotateThumb.cs
├── RoutedCommand.cs
├── SizeAdorner.cs
└── SizeChrome.cs
├── Properties
├── AssemblyInfo.cs
├── Resources.Designer.cs
├── Resources.resx
├── Settings.Designer.cs
└── Settings.settings
├── Resources
├── ButtonImageStyle.xaml
├── ColorsStyle.xaml
├── GeometryChromeStyle.xaml
├── LableStyle.xaml
├── PulseButtonStyle.xaml
├── RadioButtonStyle.xaml
├── ResizeRotateChromeStyle.xaml
├── RightTabControlStyle.xaml
├── SizeChrome.xaml
└── ToolBarImage.xaml
└── app.manifest
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/.github/workflows/dotnet-build.yml:
--------------------------------------------------------------------------------
1 | name: .NET Build
2 |
3 | on: [push, pull_request]
4 |
5 | jobs:
6 | build:
7 |
8 | runs-on: windows-latest
9 |
10 | steps:
11 | - uses: actions/checkout@v1
12 |
13 | - name: Setup .NET
14 | uses: actions/setup-dotnet@v1
15 | with:
16 | dotnet-version: |
17 | 3.1.x
18 | 5.0.x
19 | 6.0.x
20 |
21 | - name: Build with dotnet
22 | run: dotnet build --configuration Release
23 |
24 | - name: Test
25 | run: dotnet test --configuration Release
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Mono auto generated files
17 | mono_crash.*
18 |
19 | # Build results
20 | [Dd]ebug/
21 | [Dd]ebugPublic/
22 | [Rr]elease/
23 | [Rr]eleases/
24 | x64/
25 | x86/
26 | [Ww][Ii][Nn]32/
27 | [Aa][Rr][Mm]/
28 | [Aa][Rr][Mm]64/
29 | bld/
30 | [Bb]in/
31 | [Oo]bj/
32 | [Oo]ut/
33 | [Ll]og/
34 | [Ll]ogs/
35 |
36 | # Visual Studio 2015/2017 cache/options directory
37 | .vs/
38 | # Uncomment if you have tasks that create the project's static files in wwwroot
39 | #wwwroot/
40 |
41 | # Visual Studio 2017 auto generated files
42 | Generated\ Files/
43 |
44 | # MSTest test Results
45 | [Tt]est[Rr]esult*/
46 | [Bb]uild[Ll]og.*
47 |
48 | # NUnit
49 | *.VisualState.xml
50 | TestResult.xml
51 | nunit-*.xml
52 |
53 | # Build Results of an ATL Project
54 | [Dd]ebugPS/
55 | [Rr]eleasePS/
56 | dlldata.c
57 |
58 | # Benchmark Results
59 | BenchmarkDotNet.Artifacts/
60 |
61 | # .NET Core
62 | project.lock.json
63 | project.fragment.lock.json
64 | artifacts/
65 |
66 | # ASP.NET Scaffolding
67 | ScaffoldingReadMe.txt
68 |
69 | # StyleCop
70 | StyleCopReport.xml
71 |
72 | # Files built by Visual Studio
73 | *_i.c
74 | *_p.c
75 | *_h.h
76 | *.ilk
77 | *.meta
78 | *.obj
79 | *.iobj
80 | *.pch
81 | *.pdb
82 | *.ipdb
83 | *.pgc
84 | *.pgd
85 | *.rsp
86 | *.sbr
87 | *.tlb
88 | *.tli
89 | *.tlh
90 | *.tmp
91 | *.tmp_proj
92 | *_wpftmp.csproj
93 | *.log
94 | *.vspscc
95 | *.vssscc
96 | .builds
97 | *.pidb
98 | *.svclog
99 | *.scc
100 |
101 | # Chutzpah Test files
102 | _Chutzpah*
103 |
104 | # Visual C++ cache files
105 | ipch/
106 | *.aps
107 | *.ncb
108 | *.opendb
109 | *.opensdf
110 | *.sdf
111 | *.cachefile
112 | *.VC.db
113 | *.VC.VC.opendb
114 |
115 | # Visual Studio profiler
116 | *.psess
117 | *.vsp
118 | *.vspx
119 | *.sap
120 |
121 | # Visual Studio Trace Files
122 | *.e2e
123 |
124 | # TFS 2012 Local Workspace
125 | $tf/
126 |
127 | # Guidance Automation Toolkit
128 | *.gpState
129 |
130 | # ReSharper is a .NET coding add-in
131 | _ReSharper*/
132 | *.[Rr]e[Ss]harper
133 | *.DotSettings.user
134 |
135 | # TeamCity is a build add-in
136 | _TeamCity*
137 |
138 | # DotCover is a Code Coverage Tool
139 | *.dotCover
140 |
141 | # AxoCover is a Code Coverage Tool
142 | .axoCover/*
143 | !.axoCover/settings.json
144 |
145 | # Coverlet is a free, cross platform Code Coverage Tool
146 | coverage*.json
147 | coverage*.xml
148 | coverage*.info
149 |
150 | # Visual Studio code coverage results
151 | *.coverage
152 | *.coveragexml
153 |
154 | # NCrunch
155 | _NCrunch_*
156 | .*crunch*.local.xml
157 | nCrunchTemp_*
158 |
159 | # MightyMoose
160 | *.mm.*
161 | AutoTest.Net/
162 |
163 | # Web workbench (sass)
164 | .sass-cache/
165 |
166 | # Installshield output folder
167 | [Ee]xpress/
168 |
169 | # DocProject is a documentation generator add-in
170 | DocProject/buildhelp/
171 | DocProject/Help/*.HxT
172 | DocProject/Help/*.HxC
173 | DocProject/Help/*.hhc
174 | DocProject/Help/*.hhk
175 | DocProject/Help/*.hhp
176 | DocProject/Help/Html2
177 | DocProject/Help/html
178 |
179 | # Click-Once directory
180 | publish/
181 |
182 | # Publish Web Output
183 | *.[Pp]ublish.xml
184 | *.azurePubxml
185 | # Note: Comment the next line if you want to checkin your web deploy settings,
186 | # but database connection strings (with potential passwords) will be unencrypted
187 | *.pubxml
188 | *.publishproj
189 |
190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
191 | # checkin your Azure Web App publish settings, but sensitive information contained
192 | # in these scripts will be unencrypted
193 | PublishScripts/
194 |
195 | # NuGet Packages
196 | *.nupkg
197 | # NuGet Symbol Packages
198 | *.snupkg
199 | # The packages folder can be ignored because of Package Restore
200 | **/[Pp]ackages/*
201 | # except build/, which is used as an MSBuild target.
202 | !**/[Pp]ackages/build/
203 | # Uncomment if necessary however generally it will be regenerated when needed
204 | #!**/[Pp]ackages/repositories.config
205 | # NuGet v3's project.json files produces more ignorable files
206 | *.nuget.props
207 | *.nuget.targets
208 |
209 | # Microsoft Azure Build Output
210 | csx/
211 | *.build.csdef
212 |
213 | # Microsoft Azure Emulator
214 | ecf/
215 | rcf/
216 |
217 | # Windows Store app package directories and files
218 | AppPackages/
219 | BundleArtifacts/
220 | Package.StoreAssociation.xml
221 | _pkginfo.txt
222 | *.appx
223 | *.appxbundle
224 | *.appxupload
225 |
226 | # Visual Studio cache files
227 | # files ending in .cache can be ignored
228 | *.[Cc]ache
229 | # but keep track of directories ending in .cache
230 | !?*.[Cc]ache/
231 |
232 | # Others
233 | ClientBin/
234 | ~$*
235 | *~
236 | *.dbmdl
237 | *.dbproj.schemaview
238 | *.jfm
239 | *.pfx
240 | *.publishsettings
241 | orleans.codegen.cs
242 |
243 | # Including strong name files can present a security risk
244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
245 | #*.snk
246 |
247 | # Since there are multiple workflows, uncomment next line to ignore bower_components
248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
249 | #bower_components/
250 |
251 | # RIA/Silverlight projects
252 | Generated_Code/
253 |
254 | # Backup & report files from converting an old project file
255 | # to a newer Visual Studio version. Backup files are not needed,
256 | # because we have git ;-)
257 | _UpgradeReport_Files/
258 | Backup*/
259 | UpgradeLog*.XML
260 | UpgradeLog*.htm
261 | ServiceFabricBackup/
262 | *.rptproj.bak
263 |
264 | # SQL Server files
265 | *.mdf
266 | *.ldf
267 | *.ndf
268 |
269 | # Business Intelligence projects
270 | *.rdl.data
271 | *.bim.layout
272 | *.bim_*.settings
273 | *.rptproj.rsuser
274 | *- [Bb]ackup.rdl
275 | *- [Bb]ackup ([0-9]).rdl
276 | *- [Bb]ackup ([0-9][0-9]).rdl
277 |
278 | # Microsoft Fakes
279 | FakesAssemblies/
280 |
281 | # GhostDoc plugin setting file
282 | *.GhostDoc.xml
283 |
284 | # Node.js Tools for Visual Studio
285 | .ntvs_analysis.dat
286 | node_modules/
287 |
288 | # Visual Studio 6 build log
289 | *.plg
290 |
291 | # Visual Studio 6 workspace options file
292 | *.opt
293 |
294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
295 | *.vbw
296 |
297 | # Visual Studio LightSwitch build output
298 | **/*.HTMLClient/GeneratedArtifacts
299 | **/*.DesktopClient/GeneratedArtifacts
300 | **/*.DesktopClient/ModelManifest.xml
301 | **/*.Server/GeneratedArtifacts
302 | **/*.Server/ModelManifest.xml
303 | _Pvt_Extensions
304 |
305 | # Paket dependency manager
306 | .paket/paket.exe
307 | paket-files/
308 |
309 | # FAKE - F# Make
310 | .fake/
311 |
312 | # CodeRush personal settings
313 | .cr/personal
314 |
315 | # Python Tools for Visual Studio (PTVS)
316 | __pycache__/
317 | *.pyc
318 |
319 | # Cake - Uncomment if you are using it
320 | # tools/**
321 | # !tools/packages.config
322 |
323 | # Tabs Studio
324 | *.tss
325 |
326 | # Telerik's JustMock configuration file
327 | *.jmconfig
328 |
329 | # BizTalk build output
330 | *.btp.cs
331 | *.btm.cs
332 | *.odx.cs
333 | *.xsd.cs
334 |
335 | # OpenCover UI analysis results
336 | OpenCover/
337 |
338 | # Azure Stream Analytics local run output
339 | ASALocalRun/
340 |
341 | # MSBuild Binary and Structured Log
342 | *.binlog
343 |
344 | # NVidia Nsight GPU debugger configuration file
345 | *.nvuser
346 |
347 | # MFractors (Xamarin productivity tool) working folder
348 | .mfractor/
349 |
350 | # Local History for Visual Studio
351 | .localhistory/
352 |
353 | # BeatPulse healthcheck temp database
354 | healthchecksdb
355 |
356 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
357 | MigrationBackup/
358 |
359 | # Ionide (cross platform F# VS Code tools) working folder
360 | .ionide/
361 |
362 | # Fody - auto-generated XML schema
363 | FodyWeavers.xsd
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # GeometryToolDemo
--------------------------------------------------------------------------------
/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | latest
5 | $(MSBuildThisFileDirectory)bin\$(Configuration)
6 | dotnet campus(.NET 职业技术学院)
7 |
8 |
9 | dotnet-campus
10 | https://github.com/dotnet-campus/GeometryToolDemo
11 | https://github.com/dotnet-campus/GeometryToolDemo
12 | TODO: 请在此处添加包描述信息。
13 |
14 | git
15 | Copyright © 2021 dotnet campus, All Rights Reserved.
16 |
17 |
--------------------------------------------------------------------------------
/GeometryToolDemo.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.0.32014.148
5 | MinimumVisualStudioVersion = 15.0.26124.0
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GeometryTool", "src\GeometryTool\GeometryTool\GeometryTool.csproj", "{06B492A3-8225-4497-91A2-3D5FB4DEF45E}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {06B492A3-8225-4497-91A2-3D5FB4DEF45E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {06B492A3-8225-4497-91A2-3D5FB4DEF45E}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {06B492A3-8225-4497-91A2-3D5FB4DEF45E}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {06B492A3-8225-4497-91A2-3D5FB4DEF45E}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | GlobalSection(ExtensibilityGlobals) = postSolution
23 | SolutionGuid = {B4BBE725-CB3C-4EA5-8754-B5A230B27F29}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2015-2022 [YuWeiCong](https://github.com/YuWeiCong) and dotnet campus
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # GeometryToolDemo
2 |
3 | 
4 |
5 | 二维绘画小工具
6 |
7 | 
8 |
9 | 
10 |
11 | 
12 |
13 | 
14 |
15 | 
16 |
17 | 
18 |
19 |
--------------------------------------------------------------------------------
/build/Version.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | 1.0.0
4 |
5 |
--------------------------------------------------------------------------------
/docs/image/README/README0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotnet-campus/GeometryToolDemo/b488b66099e0de4c96ca4a184e56d51084ffd2f8/docs/image/README/README0.png
--------------------------------------------------------------------------------
/docs/image/README/README1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotnet-campus/GeometryToolDemo/b488b66099e0de4c96ca4a184e56d51084ffd2f8/docs/image/README/README1.png
--------------------------------------------------------------------------------
/docs/image/README/README2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotnet-campus/GeometryToolDemo/b488b66099e0de4c96ca4a184e56d51084ffd2f8/docs/image/README/README2.png
--------------------------------------------------------------------------------
/docs/image/README/README3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotnet-campus/GeometryToolDemo/b488b66099e0de4c96ca4a184e56d51084ffd2f8/docs/image/README/README3.png
--------------------------------------------------------------------------------
/docs/image/README/README4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotnet-campus/GeometryToolDemo/b488b66099e0de4c96ca4a184e56d51084ffd2f8/docs/image/README/README4.png
--------------------------------------------------------------------------------
/docs/image/README/README5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotnet-campus/GeometryToolDemo/b488b66099e0de4c96ca4a184e56d51084ffd2f8/docs/image/README/README5.png
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Action/BorderLock.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows;
3 | using System.Windows.Controls;
4 | using System.Windows.Data;
5 | using System.Windows.Documents;
6 | using System.Windows.Media;
7 | using System.Windows.Media.Imaging;
8 | using System.Windows.Shapes;
9 |
10 | namespace GeometryTool;
11 |
12 | ///
13 | /// 点的融合和拆分
14 | ///
15 | public class BorderLock
16 | {
17 | ///
18 | /// 用于记录要进行命中测试的图形
19 | ///
20 | private readonly BorderWithDrag _border;
21 |
22 | ///
23 | /// 用于计算命中的BorderWithDrag的数量
24 | ///
25 | private int _count;
26 |
27 | public BorderLock(BorderWithDrag border)
28 | {
29 | _border = border;
30 | }
31 |
32 | public BorderLock()
33 | {
34 | }
35 |
36 | ///
37 | /// 点的合并
38 | ///
39 | ///
40 | public void Lock(Point point)
41 | {
42 | var currentPoint = point;
43 |
44 | _border.PointList.Clear();
45 | VisualTreeHelper.HitTest(_border.Parent as Canvas, null, //进行命中测试
46 | MyHitTestResult,
47 | new PointHitTestParameters(currentPoint));
48 |
49 | if (_count > 1) //如果该点有多于两个BorderWithDrag,说明有点融合
50 | {
51 | if (_border.BrotherBorder == null)
52 | {
53 | _border.BrotherBorder = _border.PointList[1];
54 | }
55 |
56 | if (_border.BrotherBorder.LockAdornor == null) //只选择一个图层来显示Adorner
57 | {
58 | _border.BrotherBorder.LockAdornor = new LockAdorner(_border.BrotherBorder);
59 | var layer = AdornerLayer.GetAdornerLayer(_border.BrotherBorder.Parent as Canvas);
60 | if (layer != null)
61 | {
62 | layer.Add(_border.BrotherBorder.LockAdornor);
63 | }
64 | }
65 |
66 | _border.BrotherBorder.HasOtherPoint = true; //显示锁
67 | _border.BrotherBorder.LockAdornor.LockChrome.Source =
68 | new BitmapImage(new Uri("Image/lock.png", UriKind.Relative));
69 |
70 | if (_border != _border.BrotherBorder)
71 | {
72 | _border.BrotherBorder.PointList = _border.PointList;
73 | }
74 |
75 | foreach (var border in _border.BrotherBorder.PointList)
76 | {
77 | if (border != _border.BrotherBorder)
78 | {
79 | BindingOperations.ClearBinding((border.Child as Path).Data as EllipseGeometry,
80 | EllipseGeometry.CenterProperty);
81 | var binding = new Binding("Center")
82 | { Source = (_border.BrotherBorder.Child as Path).Data as EllipseGeometry };
83 | binding.Mode = BindingMode.TwoWay;
84 | BindingOperations.SetBinding((border.Child as Path).Data as EllipseGeometry,
85 | EllipseGeometry.CenterProperty, binding);
86 | }
87 | }
88 | }
89 | else
90 | {
91 | _border.HasOtherPoint = false;
92 | }
93 | }
94 |
95 | ///
96 | /// 命中测试
97 | ///
98 | ///
99 | ///
100 | public HitTestResultBehavior MyHitTestResult(HitTestResult result)
101 | {
102 | var path = result.VisualHit as Path;
103 | if (path != null)
104 | {
105 | var border = path.Parent as BorderWithDrag;
106 | if (border != null) //如果命中的图形是BorderWithDrag,就Count++
107 | {
108 | _border.PointList.Add(border);
109 | _count++;
110 | if (border.LockAdornor != null) //选择一个已经带有Adorner的Border来显示锁
111 | {
112 | _border.BrotherBorder = border;
113 | }
114 | }
115 | }
116 |
117 | return HitTestResultBehavior.Continue;
118 | }
119 |
120 | ///
121 | /// 解开点的融合
122 | ///
123 | ///
124 | public void UnLock(BorderWithDrag border)
125 | {
126 | var brotherBorder = border.BrotherBorder;
127 | var p = ((border.Child as Path).Data as EllipseGeometry).Center;
128 | foreach (var item in border.PointList) //解开所用的Binding
129 | {
130 | BindingOperations.ClearBinding((item.Child as Path).Data as EllipseGeometry,
131 | EllipseGeometry.CenterProperty);
132 | item.BrotherBorder = null;
133 | ((item.Child as Path).Data as EllipseGeometry).Center = p; //重定位到原来的位置
134 | }
135 |
136 | border.HasOtherPoint = false;
137 | }
138 | }
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Action/GeomortyStringConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using System.Text.RegularExpressions;
5 | using System.Windows;
6 | using System.Windows.Controls;
7 | using System.Windows.Data;
8 | using System.Windows.Media;
9 | using System.Windows.Shapes;
10 |
11 | namespace GeometryTool;
12 |
13 | ///
14 | /// 字符串转图形,图形转字符串
15 | ///
16 | public class GeometryStringConverter
17 | {
18 | private readonly GraphAppearance _graphAppearance;
19 | private readonly Canvas _rootCanvas;
20 |
21 | ///
22 | /// 构造函数
23 | ///
24 | ///
25 | ///
26 | public GeometryStringConverter(Canvas rootCanvas, GraphAppearance graphAppearance)
27 | {
28 | _rootCanvas = rootCanvas;
29 | _graphAppearance = graphAppearance;
30 | }
31 |
32 | ///
33 | /// 从字符串中读取图形,并绘制出来
34 | ///
35 | ///
36 | public BorderWithAdorner GeometryFromString(string vMiniLanguage)
37 | {
38 | var graphAdd = new GraphAdd();
39 | var path = new Path
40 | {
41 | Stroke = _graphAppearance.Stroke,
42 | StrokeThickness = _graphAppearance.StrokeThickness,
43 | StrokeDashArray = _graphAppearance.StrokeDashArray
44 | };
45 |
46 | if (_graphAppearance.Fill != null)
47 | {
48 | path.Fill = _graphAppearance.Fill;
49 | }
50 |
51 | var regex = new Regex(@"([a-zA-Z])\s*([^a-zA-Z]*)");
52 | var matchList = regex.Matches(vMiniLanguage);
53 |
54 | //定义直线
55 | var pathGeometry = new PathGeometry();
56 | var borderWithAdorner = new BorderWithAdorner();
57 | borderWithAdorner.Child = path;
58 | path.Data = pathGeometry;
59 | pathGeometry.Figures = new PathFigureCollection();
60 | var mPathFigure = new PathFigure();
61 | var ellipsePoint = new Point();
62 | var ellipseStartPath = new Path();
63 | var ellipseList = new List();
64 | var number = 0;
65 | BorderWithDrag border;
66 | for (var i = 0; i < matchList.Count; ++i)
67 | {
68 | //产生点
69 | number++;
70 | var match = matchList[i];
71 | if (i == 0) //处理起点的问题
72 | {
73 | if (match.Groups[1].ToString() != "M") //起点包含M
74 | {
75 | mPathFigure.StartPoint = new Point { X = 0, Y = 0 };
76 | graphAdd.AddPointWithNoBorder(ellipsePoint, _graphAppearance, _rootCanvas, out ellipseStartPath);
77 | borderWithAdorner.EllipseList.Add(ellipseStartPath);
78 | pathGeometry.Figures.Add(mPathFigure);
79 | }
80 | else //起点不包含M
81 | {
82 | //创建PathFigure,并绑定StartPoint
83 | GetEllipsePointWithNoBorder(match, out ellipseStartPath, out ellipsePoint,
84 | @"([\+\-\d\.]*),([\+\-\d\.]*)"); //构造EllipsePoint
85 | borderWithAdorner.EllipseList.Add(ellipseStartPath);
86 | if (Regex.IsMatch(vMiniLanguage, "[HLV]"))
87 | {
88 | ellipseList.Add(ellipseStartPath);
89 | border = new BorderWithDrag(path, 1, ellipseList);
90 | border.Child = ellipseStartPath;
91 | }
92 | else
93 | {
94 | border = new BorderWithDrag();
95 | border.Child = ellipseStartPath;
96 | }
97 | }
98 |
99 | var startEllipseGeometry = ellipseStartPath.Data as EllipseGeometry;
100 | pathGeometry.Figures.Clear();
101 | mPathFigure = new PathFigure();
102 | if (Regex.IsMatch(vMiniLanguage, @"\sZ"))
103 | {
104 | mPathFigure.IsClosed = true;
105 | }
106 |
107 | var binding = new Binding("Center") { Source = startEllipseGeometry }; //绑定起点
108 | binding.Mode = BindingMode.TwoWay;
109 | BindingOperations.SetBinding(mPathFigure, PathFigure.StartPointProperty, binding);
110 | pathGeometry.Figures.Add(mPathFigure);
111 | var segmentCollection = new PathSegmentCollection();
112 | mPathFigure.Segments = segmentCollection;
113 | }
114 |
115 | Path vPath;
116 |
117 | switch (match.Groups[1].ToString())
118 | {
119 | case "L":
120 | {
121 | //创建PathFigure,并绑定StartPoint
122 |
123 | GetEllipsePointWithNoBorder(match, out vPath, out ellipsePoint,
124 | @"([\+\-\d\.]*),([\+\-\d\.]*)"); //构造EllipsePoint
125 | borderWithAdorner.EllipseList.Add(vPath);
126 | //创建LineSegment,并绑定Point
127 | graphAdd.AddLineSegment(mPathFigure, vPath);
128 | ellipseList.Add(vPath);
129 | border = new BorderWithDrag(path, number, ellipseList);
130 | border.Child = vPath;
131 |
132 | break;
133 | }
134 |
135 | case "H":
136 | {
137 | ellipsePoint.X = Convert.ToDouble(match.Groups[2].ToString());
138 |
139 | graphAdd.AddPointWithNoBorder(ellipsePoint, _graphAppearance, _rootCanvas, out vPath);
140 | borderWithAdorner.EllipseList.Add(vPath);
141 | graphAdd.AddLineSegment(mPathFigure, vPath);
142 | ellipseList.Add(vPath);
143 | border = new BorderWithDrag(path, number, ellipseList);
144 | border.Child = vPath;
145 |
146 | break;
147 | }
148 |
149 | case "V":
150 | {
151 | ellipsePoint.Y = Convert.ToDouble(match.Groups[2].ToString());
152 |
153 | graphAdd.AddPointWithNoBorder(ellipsePoint, _graphAppearance, _rootCanvas, out vPath);
154 | borderWithAdorner.EllipseList.Add(vPath);
155 | graphAdd.AddLineSegment(mPathFigure, vPath);
156 | ellipseList.Add(vPath);
157 | border = new BorderWithDrag(path, number, ellipseList);
158 | border.Child = vPath;
159 |
160 | break;
161 | }
162 |
163 | case "A":
164 | {
165 | //创建LineSegment,并绑定Point
166 | var size = new Size
167 | {
168 | Width = Convert.ToDouble(Regex
169 | .Match(match.Groups[0].ToString(), @"A ([\+\-\d\.]*),([\+\-\d\.]*)").Groups[1].ToString()),
170 | Height = Convert.ToDouble(Regex
171 | .Match(match.Groups[0].ToString(), @"A ([\+\-\d\.]*),([\+\-\d\.]*)").Groups[2].ToString())
172 | };
173 |
174 | SweepDirection vSweepDirection;
175 | var isLargeArc = false;
176 | var matches = Regex.Matches(match.Groups[0].ToString(),
177 | @"A\s*[\+\-\d\.]+,[\+\-\d\.]+\s*([\+\-\.\d]+)\s*([\+\-\.\d]+)\s*([\+\-\.\d]+)");
178 | if (Convert.ToInt32(matches[0].Groups[2].ToString()) == 1) //设置SweepDirection
179 | {
180 | vSweepDirection = SweepDirection.Clockwise;
181 | }
182 | else
183 | {
184 | vSweepDirection = SweepDirection.Counterclockwise;
185 | }
186 |
187 | if (Convert.ToInt32(matches[0].Groups[3].ToString()) == 1) //设置IsLargeArc
188 | {
189 | isLargeArc = true;
190 | }
191 |
192 | //创建PathFigure,并绑定StartPoint
193 | GetEllipsePointWithNoBorder(match, out vPath, out ellipsePoint,
194 | @"\d ([\+\-\d\.]*),([\+\-\d\.]*)"); //构造EllipsePoint
195 | graphAdd.AddArcSegment(mPathFigure, vPath, size,
196 | Convert.ToDouble(matches[0].Groups[1].ToString()), vSweepDirection, isLargeArc);
197 | borderWithAdorner.EllipseList.Add(vPath);
198 | border = new BorderWithDrag();
199 | border.Child = vPath;
200 | break;
201 | }
202 |
203 | case "C":
204 | {
205 | //创建PathFigure,并绑定StartPoint
206 | Path ellipsePath1;
207 | GetEllipsePointWithNoBorder(match, out ellipsePath1, out ellipsePoint,
208 | @"([\+\-\d\.]*),([\+\-\d\.]*)"); //构造EllipsePoint
209 | borderWithAdorner.EllipseList.Add(ellipsePath1);
210 | border = new BorderWithDrag();
211 | border.Child = ellipsePath1;
212 |
213 | Path ellipsePath2;
214 | GetEllipsePointWithNoBorder(match, out ellipsePath2, out ellipsePoint,
215 | @"C\s+[\+\-\d\.]+,[\+\-\d\.]+\s*([\+\-\d\.]+),([\+\-\d\.]+)"); //构造EllipsePoint
216 | borderWithAdorner.EllipseList.Add(ellipsePath2);
217 | border = new BorderWithDrag();
218 | border.Child = ellipsePath2;
219 |
220 | GetEllipsePointWithNoBorder(match, out vPath, out ellipsePoint,
221 | @"([\+\-\d\.]+),([\+\-\d\.]+)\s*$"); //构造EllipsePoint
222 | borderWithAdorner.EllipseList.Add(vPath);
223 | graphAdd.AddBezierSegment(mPathFigure, ellipsePath1, ellipsePath2, vPath);
224 | border = new BorderWithDrag();
225 | border.Child = vPath;
226 |
227 | break;
228 | }
229 |
230 | case "Q":
231 | {
232 | //创建PathFigure,并绑定StartPoint
233 | Path ellipsePath1;
234 | GetEllipsePointWithNoBorder(match, out ellipsePath1, out ellipsePoint,
235 | @"([\+\-\d\.]*),([\+\-\d\.]*)"); //构造EllipsePoint
236 | borderWithAdorner.EllipseList.Add(ellipsePath1);
237 | border = new BorderWithDrag();
238 | border.Child = ellipsePath1;
239 |
240 | GetEllipsePointWithNoBorder(match, out vPath, out ellipsePoint,
241 | @"\d\s+([\+\-\d\.]*),([\+\-\d\.]*)"); //构造EllipsePoint
242 | borderWithAdorner.EllipseList.Add(vPath);
243 | graphAdd.AddQuadraticSegment(mPathFigure, ellipsePath1, vPath);
244 | border = new BorderWithDrag();
245 | border.Child = vPath;
246 |
247 | break;
248 | }
249 | }
250 | }
251 |
252 | return borderWithAdorner;
253 | }
254 |
255 | ///
256 | /// 把图形转换成为字符串
257 | ///
258 | ///
259 | ///
260 | public string StringFromGeometry(Path vPath)
261 | {
262 | var miniLanguage = new StringBuilder();
263 | var pathGeometry = vPath.Data as PathGeometry;
264 | var pathFigure = pathGeometry.Figures[0];
265 | var segmentCol = pathFigure.Segments;
266 | miniLanguage.Append("M " + pathFigure.StartPoint.X + "," + pathFigure.StartPoint.Y + " ");
267 |
268 | foreach (var item in segmentCol)
269 | {
270 | if (item.GetType() == typeof(LineSegment))
271 | {
272 | miniLanguage.Append("L " + (item as LineSegment).Point.X + "," + (item as LineSegment).Point.Y + " ");
273 | }
274 | else if (item.GetType() == typeof(ArcSegment))
275 | {
276 | var arcSegment = item as ArcSegment;
277 | miniLanguage.Append("A " + arcSegment.Size.Width + "," + arcSegment.Size.Height + " " +
278 | arcSegment.RotationAngle + " ");
279 | miniLanguage.Append((arcSegment.IsLargeArc ? 1 : 0) + " " +
280 | (arcSegment.SweepDirection == SweepDirection.Clockwise ? 1 : 0) + " ");
281 | miniLanguage.Append(arcSegment.Point.X + "," + arcSegment.Point.Y + " ");
282 | }
283 | else if (item.GetType() == typeof(BezierSegment))
284 | {
285 | var bezierSegment = item as BezierSegment;
286 | miniLanguage.Append("C " + bezierSegment.Point1.X + "," + bezierSegment.Point1.Y + " ");
287 | miniLanguage.Append(bezierSegment.Point2.X + "," + bezierSegment.Point2.Y + " ");
288 | miniLanguage.Append(bezierSegment.Point3.X + "," + bezierSegment.Point3.Y + " ");
289 | }
290 | else if (item.GetType() == typeof(QuadraticBezierSegment))
291 | {
292 | var QBezierSegment = item as QuadraticBezierSegment;
293 | miniLanguage.Append("Q " + QBezierSegment.Point1.X + "," + QBezierSegment.Point1.Y + " ");
294 | miniLanguage.Append(QBezierSegment.Point2.X + "," + QBezierSegment.Point2.Y + " ");
295 | }
296 | }
297 |
298 | if (pathFigure.IsClosed)
299 | {
300 | miniLanguage.Append("Z ");
301 | }
302 |
303 | return miniLanguage.ToString();
304 | }
305 |
306 | ///
307 | /// 产生一个EllipsePoint
308 | ///
309 | private void GetEllipsePoint(Match match, out Path ellipsePath, out Point ellipsePoint, string vPattern)
310 | {
311 | var graphAdd = new GraphAdd();
312 | ellipsePoint = new Point();
313 | ellipsePoint.X = Convert.ToDouble(Regex.Match(match.Groups[0].ToString(), vPattern).Groups[1].ToString());
314 | ellipsePoint.Y = Convert.ToDouble(Regex.Match(match.Groups[0].ToString(), vPattern).Groups[2].ToString());
315 | ellipsePath = new Path();
316 | graphAdd.AddPoint(ellipsePoint, _graphAppearance, _rootCanvas, out ellipsePath);
317 | }
318 |
319 | ///
320 | /// 产生一个带BorderWithAdorner的Ellipse
321 | ///
322 | ///
323 | ///
324 | ///
325 | ///
326 | private void GetEllipsePointWithNoBorder(Match match, out Path ellipsePath, out Point ellipsePoint, string vPattern)
327 | {
328 | var graphAdd = new GraphAdd();
329 | ellipsePoint = new Point();
330 | ellipsePoint.X = Convert.ToDouble(Regex.Match(match.Groups[0].ToString(), vPattern).Groups[1].ToString());
331 | ellipsePoint.Y = Convert.ToDouble(Regex.Match(match.Groups[0].ToString(), vPattern).Groups[2].ToString());
332 | ellipsePath = new Path();
333 | graphAdd.AddPointWithNoBorder(ellipsePoint, _graphAppearance, _rootCanvas, out ellipsePath);
334 | }
335 |
336 | ///
337 | /// 构造PathGeometry
338 | ///
339 | ///
340 | ///
341 | public string StringFromPathGeometry(Path vPath)
342 | {
343 | var stringBuilder = new StringBuilder();
344 | stringBuilder.AppendLine(" ");
345 | stringBuilder.AppendLine(" ");
346 | stringBuilder.AppendLine(" ");
348 | stringBuilder.AppendLine(" ");
349 | stringBuilder.AppendLine(" ");
350 | stringBuilder.AppendLine(" ");
352 | stringBuilder.AppendLine(" ");
353 | stringBuilder.Append(" ");
354 | return stringBuilder.ToString();
355 | }
356 |
357 | ///
358 | /// 从XML中读取图形
359 | ///
360 | ///
361 | ///
362 | public List PathGeometryFromString(string vXMLString)
363 | {
364 | var pattern =
365 | @"\s*\s*\s*\s*\s*";
366 | var matchList = Regex.Matches(vXMLString, pattern);
367 | var borderWithAdornerList = new List();
368 | foreach (Match item in matchList)
369 | {
370 | var backgroundColor = item.Groups[1].ToString();
371 | var strokeColor = item.Groups[4].ToString();
372 | var miniLanguage = item.Groups[2].ToString();
373 | var strokeThickness = item.Groups[3].ToString();
374 | var graphAppearance = new GraphAppearance();
375 | graphAppearance.Fill = new SolidColorBrush((Color)ColorConverter.ConvertFromString(backgroundColor));
376 | graphAppearance.Stroke = new SolidColorBrush((Color)ColorConverter.ConvertFromString(strokeColor));
377 | graphAppearance.StrokeThickness = Convert.ToDouble(strokeThickness);
378 | var geometryStringConverter = new GeometryStringConverter(MainWindow.MyRootCanvas, graphAppearance);
379 | var newBorderWithAdorner =
380 | geometryStringConverter.GeometryFromString(miniLanguage); //把Mini-Language转化成为图形,实现图形的深度复制
381 | var newPath = newBorderWithAdorner.Child as Path;
382 | newPath.Stroke = graphAppearance.Stroke;
383 | newPath.StrokeThickness = graphAppearance.StrokeThickness;
384 | newPath.Fill = graphAppearance.Fill;
385 | borderWithAdornerList.Add(newBorderWithAdorner);
386 | }
387 |
388 | return borderWithAdornerList;
389 | }
390 | }
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Action/GraphAdd.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 | using System.Windows.Controls;
3 | using System.Windows.Data;
4 | using System.Windows.Media;
5 | using System.Windows.Shapes;
6 |
7 | namespace GeometryTool;
8 |
9 | ///
10 | /// 用于绘制图形,如点,直线,三角形,矩形,圆,椭圆
11 | ///
12 | public class GraphAdd
13 | {
14 | ///
15 | /// 包装Path的border
16 | ///
17 | private BorderWithDrag _border;
18 |
19 | private BorderWithAdorner _borderWithAdorner;
20 |
21 | ///
22 | /// 新建一个PathGeometry
23 | ///
24 | ///
25 | ///
26 | ///
27 | ///
28 | ///
29 | public void NewPathGeometry(GraphAppearance vGraphAppearance, Canvas vRootCanvas, out Path vPath, Path vEllipsePath,
30 | bool vIsClose)
31 | {
32 | //设置直线的外观
33 | vPath = new Path
34 | {
35 | Stroke = vGraphAppearance.Stroke,
36 | StrokeThickness = vGraphAppearance.StrokeThickness
37 | };
38 |
39 | if (vGraphAppearance.Fill != Brushes.Transparent)
40 | {
41 | vPath.Fill = vGraphAppearance.Fill;
42 | }
43 |
44 | var pathGeometry = new PathGeometry();
45 | vPath.Data = pathGeometry;
46 | pathGeometry.FillRule = vGraphAppearance.FillRule;
47 | pathGeometry.Figures = new PathFigureCollection();
48 | var pathFigure = new PathFigure { IsClosed = vIsClose };
49 | pathGeometry.Figures.Add(pathFigure);
50 | var segmentCollection = new PathSegmentCollection();
51 | pathFigure.Segments = segmentCollection;
52 |
53 | //设置起点绑定该Point
54 | var e = vEllipsePath.Data as EllipseGeometry;
55 | var binding = new Binding("Center") { Source = e };
56 | binding.Mode = BindingMode.TwoWay;
57 | BindingOperations.SetBinding(pathFigure, PathFigure.StartPointProperty, binding);
58 | }
59 |
60 | ///
61 | /// 添加一个LineSegment
62 | ///
63 | ///
64 | ///
65 | public void AddLineSegment(PathFigure vPathFigure, Path vEllipsePath)
66 | {
67 | var e = (EllipseGeometry)vEllipsePath.Data; //获取要绑定的点
68 | var newLineSegment = new LineSegment();
69 | var binding = new Binding("Center") { Source = e }; //绑定点
70 | binding.Mode = BindingMode.TwoWay;
71 | BindingOperations.SetBinding(newLineSegment, LineSegment.PointProperty, binding);
72 | vPathFigure.Segments.Add(newLineSegment);
73 | }
74 |
75 | ///
76 | /// 添加一条ArgSegment
77 | ///
78 | public void AddArcSegment(PathFigure vPathFigure, Path vEllipsePath, Size vSize, double vRotationAngle,
79 | SweepDirection vSweepDirection, bool vIsLargeArc)
80 | {
81 | var e = (EllipseGeometry)vEllipsePath.Data; //获取要绑定的点
82 | var newArcSegment = new ArcSegment();
83 | newArcSegment.Size = vSize;
84 | newArcSegment.SweepDirection = vSweepDirection;
85 | newArcSegment.IsLargeArc = vIsLargeArc;
86 | newArcSegment.RotationAngle = vRotationAngle;
87 | var binding = new Binding("Center") { Source = e }; //绑定点
88 | binding.Mode = BindingMode.TwoWay;
89 | BindingOperations.SetBinding(newArcSegment, ArcSegment.PointProperty, binding);
90 | vPathFigure.Segments.Add(newArcSegment);
91 | }
92 |
93 | ///
94 | /// 三次方贝塞尔曲线
95 | ///
96 | ///
97 | ///
98 | ///
99 | ///
100 | public void AddBezierSegment(PathFigure vPathFigure, Path vEllipsePath1, Path vEllipsePath2, Path vEllipsePath3)
101 | {
102 | var e1 = (EllipseGeometry)vEllipsePath1.Data; //获取要绑定的点1
103 | var e2 = (EllipseGeometry)vEllipsePath2.Data; //获取要绑定的点2
104 | var e3 = (EllipseGeometry)vEllipsePath3.Data; //获取要绑定的点3
105 | var newLineSegment = new BezierSegment();
106 |
107 | var binding1 = new Binding("Center") { Source = e1 }; //绑定点1
108 | binding1.Mode = BindingMode.TwoWay;
109 | BindingOperations.SetBinding(newLineSegment, BezierSegment.Point1Property, binding1);
110 |
111 | var binding2 = new Binding("Center") { Source = e2 }; //绑定点2
112 | binding2.Mode = BindingMode.TwoWay;
113 | BindingOperations.SetBinding(newLineSegment, BezierSegment.Point2Property, binding2);
114 |
115 | var binding3 = new Binding("Center") { Source = e3 }; //绑定点3
116 | binding3.Mode = BindingMode.TwoWay;
117 | BindingOperations.SetBinding(newLineSegment, BezierSegment.Point3Property, binding3);
118 |
119 | vPathFigure.Segments.Add(newLineSegment);
120 | }
121 |
122 | ///
123 | /// 两次方贝塞尔曲线
124 | ///
125 | ///
126 | ///
127 | ///
128 | public void AddQuadraticSegment(PathFigure vPathFigure, Path vEllipsePath1, Path vEllipsePath2)
129 | {
130 | var e1 = (EllipseGeometry)vEllipsePath1.Data; //获取要绑定的点1
131 | var e2 = (EllipseGeometry)vEllipsePath2.Data; //获取要绑定的点2
132 | var newLineSegment = new QuadraticBezierSegment();
133 |
134 | var binding1 = new Binding("Center") { Source = e1 }; //绑定点1
135 | binding1.Mode = BindingMode.TwoWay;
136 | BindingOperations.SetBinding(newLineSegment, QuadraticBezierSegment.Point1Property, binding1);
137 |
138 | var binding2 = new Binding("Center") { Source = e2 }; //绑定点2
139 | binding2.Mode = BindingMode.TwoWay;
140 | BindingOperations.SetBinding(newLineSegment, QuadraticBezierSegment.Point2Property, binding2);
141 |
142 | vPathFigure.Segments.Add(newLineSegment);
143 | }
144 |
145 | ///
146 | /// 绘制一个随鼠标移动的线段
147 | ///
148 | ///
149 | ///
150 | public void AddHorvorLine(PathFigure vPathFigure, Point vPoint)
151 | {
152 | var lastLine = vPathFigure.Segments[vPathFigure.Segments.Count - 1] as LineSegment;
153 | lastLine.Point = vPoint;
154 | }
155 |
156 | ///
157 | /// 画一条直线
158 | ///
159 | /// 直线的外观
160 | /// 直线的容器
161 | /// 为了修改终点而是用的变量
162 | /// 鼠标点击的图形
163 | /// 表示是起点还是终点
164 | public void AddLine(GraphAppearance vGraphAppearance, Canvas vRootCanvas, ref Path vPath, Path vHitPath,
165 | ref int vIsStartPoint, ref PathFigure vPathFigure, bool isClose)
166 | {
167 | if (vIsStartPoint == 0) //表示起点
168 | {
169 | //设置直线的外观
170 | vPath = new Path
171 | {
172 | Stroke = vGraphAppearance.Stroke,
173 | StrokeThickness = vGraphAppearance.StrokeThickness,
174 | StrokeDashArray = vGraphAppearance.StrokeDashArray
175 | };
176 |
177 | if (vGraphAppearance.Fill != Brushes.Transparent)
178 | {
179 | vPath.Fill = vGraphAppearance.Fill;
180 | }
181 |
182 | //定义直线
183 | var pathGeometry = new PathGeometry();
184 | pathGeometry.Figures = new PathFigureCollection();
185 | vPathFigure = new PathFigure { IsClosed = isClose };
186 | pathGeometry.Figures.Add(vPathFigure);
187 | var segmentCollection = new PathSegmentCollection();
188 | vPathFigure.Segments = segmentCollection;
189 | var lineSegment = new LineSegment();
190 |
191 | vPathFigure.Segments.Add(lineSegment);
192 | vPath.Data = pathGeometry;
193 |
194 | //将直线的起点和某个点绑定在一起
195 | var e = vHitPath.Data as EllipseGeometry;
196 | var binding = new Binding("Center") { Source = e };
197 | binding.Mode = BindingMode.TwoWay;
198 | BindingOperations.SetBinding(vPathFigure, PathFigure.StartPointProperty, binding);
199 | lineSegment.Point = e.Center;
200 | _borderWithAdorner = new BorderWithAdorner();
201 | _borderWithAdorner.EllipseList.Add(vHitPath);
202 | _borderWithAdorner.Child = vPath;
203 | vRootCanvas.Children.Add(_borderWithAdorner);
204 | vIsStartPoint = 1;
205 | }
206 | else //表示终点
207 | {
208 | //将直线的终点和某个点绑定在一起
209 | var lineSegment = vPathFigure.Segments[vPathFigure.Segments.Count - 1] as LineSegment;
210 | var e = vHitPath.Data as EllipseGeometry;
211 | var binding1 = new Binding("Center") { Source = e };
212 | BindingOperations.SetBinding(lineSegment, LineSegment.PointProperty, binding1);
213 |
214 | var newlineSegment = new LineSegment();
215 | newlineSegment.Point = new Point { X = e.Center.X, Y = e.Center.Y };
216 | vPathFigure.Segments.Add(newlineSegment);
217 |
218 | _borderWithAdorner.EllipseList.Add(vHitPath);
219 | }
220 | }
221 |
222 | ///
223 | /// 在面板上面画一个圆
224 | ///
225 | ///
226 | ///
227 | ///
228 | public void AddPoint(Point vPoint, GraphAppearance vGraphAppearance, Canvas vRootCanvas, out Path vPath)
229 | {
230 | //1.设置圆的外观
231 | vPath = new Path();
232 | vPath.Stroke = Brushes.Black;
233 | vPath.Fill = Brushes.White;
234 | vPath.StrokeThickness = 0.1;
235 | var ellipse = new EllipseGeometry();
236 | ellipse.RadiusX = 0.2;
237 | ellipse.RadiusY = 0.2;
238 | ellipse.Center = vPoint;
239 | vPath.Data = ellipse;
240 |
241 | //把圆放进border里面,因为Border中有装饰器,同时可以使圆可以被拖动
242 | _border = new BorderWithDrag();
243 |
244 | _border.Child = vPath;
245 | vRootCanvas.Children.Add(_border);
246 | }
247 |
248 | ///
249 | /// 在面板上面画一个圆
250 | ///
251 | ///
252 | ///
253 | ///
254 | public void AddPointWithNoBorder(Point vPoint, GraphAppearance vGraphAppearance, Canvas vRootCanvas, out Path vPath)
255 | {
256 | //1.设置圆的外观
257 | vPath = new Path();
258 | vPath.Stroke = Brushes.Black;
259 | vPath.Fill = Brushes.White;
260 | vPath.StrokeThickness = 0.1;
261 | var ellipse = new EllipseGeometry();
262 | ellipse.RadiusX = 0.2;
263 | ellipse.RadiusY = 0.2;
264 | ellipse.Center = vPoint;
265 | vPath.Data = ellipse;
266 | }
267 |
268 | ///
269 | /// 绘制一个圆
270 | ///
271 | ///
272 | ///
273 | ///
274 | ///
275 | public BorderWithAdorner AddGeometryOfCircle(Point vPoint, GraphAppearance vGraphAppearance, Canvas vRootCanvas,
276 | out Path vPath)
277 | {
278 | //设置直线的外观
279 | vPath = new Path
280 | {
281 | Stroke = vGraphAppearance.Stroke,
282 | StrokeThickness = vGraphAppearance.StrokeThickness,
283 | StrokeDashArray = vGraphAppearance.StrokeDashArray
284 | };
285 |
286 | if (vGraphAppearance.Fill != Brushes.Transparent)
287 | {
288 | vPath.Fill = vGraphAppearance.Fill;
289 | }
290 |
291 | //定义第一个PathFigure
292 | var pathGeometry = new PathGeometry();
293 | vPath.Data = pathGeometry;
294 | var borderWithAdorner = new BorderWithAdorner();
295 | borderWithAdorner.Child = vPath;
296 | vRootCanvas.Children.Add(borderWithAdorner);
297 | pathGeometry.Figures = new PathFigureCollection();
298 | var pathFigure = new PathFigure();
299 | pathGeometry.Figures.Add(pathFigure);
300 | var segmentCollection = new PathSegmentCollection();
301 | pathFigure.Segments = segmentCollection;
302 |
303 | //绘制第一个点,并绑定起点
304 | Path ellipsePath;
305 | AddPoint(vPoint, vGraphAppearance, vRootCanvas, out ellipsePath);
306 | borderWithAdorner.EllipseList.Add(ellipsePath);
307 | var ellipseGeometry = ellipsePath.Data as EllipseGeometry;
308 | var startGeometry = ellipseGeometry;
309 | var firstPointBinding = new Binding("Center") { Source = ellipseGeometry };
310 | firstPointBinding.Mode = BindingMode.TwoWay;
311 | BindingOperations.SetBinding(pathFigure, PathFigure.StartPointProperty, firstPointBinding);
312 |
313 | //绘制第二个点和第一条曲线,并绑定终点
314 | AddPoint(vPoint, vGraphAppearance, vRootCanvas, out ellipsePath); //添加点
315 | borderWithAdorner.EllipseList.Add(ellipsePath);
316 | ellipseGeometry = ellipsePath.Data as EllipseGeometry;
317 | var firstLineSe = new ArcSegment();
318 | var secondPointBinding = new Binding("Center") { Source = ellipseGeometry };
319 | secondPointBinding.Mode = BindingMode.TwoWay;
320 | BindingOperations.SetBinding(firstLineSe, ArcSegment.PointProperty, secondPointBinding); //绑定Point
321 | pathFigure.Segments.Add(firstLineSe);
322 |
323 | //绘制第二条曲线,并绑定终点
324 | var secondLineSe = new ArcSegment();
325 | AddPoint(vPoint, vGraphAppearance, vRootCanvas, out ellipsePath); //添加点
326 | borderWithAdorner.EllipseList.Add(ellipsePath);
327 | ellipseGeometry = ellipsePath.Data as EllipseGeometry;
328 | var forthPointBinding = new Binding("Center") { Source = ellipseGeometry };
329 | forthPointBinding.Mode = BindingMode.TwoWay;
330 | BindingOperations.SetBinding(secondLineSe, ArcSegment.PointProperty, forthPointBinding); //绑定Point
331 | pathFigure.Segments.Add(secondLineSe);
332 | var fiveBinding = new Binding("Center") { Source = startGeometry, Mode = BindingMode.TwoWay };
333 | BindingOperations.SetBinding(ellipseGeometry, EllipseGeometry.CenterProperty, fiveBinding);
334 |
335 | secondLineSe.Size = new Size { Height = 0.1, Width = 0.1 };
336 | firstLineSe.Size = new Size { Height = 0.1, Width = 0.1 };
337 | return borderWithAdorner;
338 | }
339 | }
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Action/XMLHelper.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Text.RegularExpressions;
3 |
4 | namespace GeometryTool;
5 |
6 | public class XMLHelper
7 | {
8 | ///
9 | /// 读取xml文件,返回图形所在Match
10 | ///
11 | ///
12 | public Match ReadXml(string vPath)
13 | {
14 | var streamReader = new StreamReader(vPath);
15 | var regex = new Regex(@"\s*([^F]*)");
16 | var match = regex.Match(streamReader.ReadToEnd());
17 | streamReader.Close();
18 | return match;
19 | }
20 |
21 | ///
22 | /// 把图形写入XML文件中写入XML
23 | ///
24 | ///
25 | ///
26 | public void WriteXml(string vPath, string vGeometryString)
27 | {
28 | var streamWriter = new StreamWriter(vPath);
29 | streamWriter.Write(vGeometryString);
30 | streamWriter.Close();
31 | }
32 | }
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/App.xaml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/App.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 |
3 | namespace GeometryTool;
4 |
5 | ///
6 | /// App.xaml 的交互逻辑
7 | ///
8 | public partial class App : Application
9 | {
10 | }
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Converter/BorderWidthHeightConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Globalization;
3 | using System.Windows.Data;
4 |
5 | namespace GeometryTool;
6 |
7 | ///
8 | /// 用于使画板自动适应屏幕的大小
9 | ///
10 | public class BorderWidthHeightConverter : IValueConverter
11 | {
12 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
13 | {
14 | if ((double)value > 0)
15 | {
16 | return (double)value - System.Convert.ToDouble(parameter);
17 | }
18 |
19 | return 0;
20 | }
21 |
22 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
23 | {
24 | return null;
25 | }
26 | }
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Converter/CanvasScaleTransformConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Globalization;
3 | using System.Windows.Data;
4 |
5 | namespace GeometryTool;
6 |
7 | ///
8 | /// 画板的缩放的Converter
9 | ///
10 | public class CanvasScaleTransformConverter : IValueConverter
11 | {
12 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
13 | {
14 | if ((double)value > 0)
15 | {
16 | return System.Convert.ToDouble(string.Format("{0:F}", (double)value)); //等于Slider的Value的十分之一
17 | }
18 |
19 | return 0;
20 | }
21 |
22 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
23 | {
24 | return null;
25 | }
26 | }
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Converter/ImageVisibilityConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Globalization;
3 | using System.Windows;
4 | using System.Windows.Data;
5 |
6 | namespace GeometryTool;
7 |
8 | ///
9 | /// 用于控制所的显示或者隐藏
10 | ///
11 | internal class ImageVisibilityConverter : IValueConverter
12 | {
13 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
14 | {
15 | if ((bool)value)
16 | {
17 | return Visibility.Visible;
18 | }
19 |
20 | return Visibility.Hidden;
21 | }
22 |
23 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
24 | {
25 | return null;
26 | }
27 | }
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Converter/IntBoolConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Globalization;
3 | using System.Windows.Data;
4 |
5 | namespace GeometryTool;
6 |
7 | ///
8 | /// 用于Int和Bool之间的转换,其中0代表false,1代表true
9 | ///
10 | public class IntBoolConverter : IValueConverter
11 | {
12 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
13 | {
14 | try
15 | {
16 | if (value != null) //判断是否为空
17 | {
18 | if ((int)value == 0)
19 | {
20 | return false;
21 | }
22 |
23 | return true;
24 | }
25 |
26 | return false;
27 | }
28 | catch
29 | {
30 | if (value != null) //判断是否为空
31 | {
32 | if ((bool)value)
33 | {
34 | return 1;
35 | }
36 |
37 | return 0;
38 | }
39 |
40 | return 0;
41 | }
42 | }
43 |
44 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
45 | {
46 | try
47 | {
48 | if (value != null) //判断是否为空
49 | {
50 | if ((int)value == 0)
51 | {
52 | return false;
53 | }
54 |
55 | return true;
56 | }
57 |
58 | return false;
59 | }
60 | catch
61 | {
62 | if (value != null) //判断是否为空
63 | {
64 | if ((bool)value)
65 | {
66 | return 1;
67 | }
68 |
69 | return 0;
70 | }
71 |
72 | return 0;
73 | }
74 | }
75 | }
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Converter/ScaleCenterConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Globalization;
3 | using System.Windows.Data;
4 |
5 | namespace GeometryTool;
6 |
7 | ///
8 | /// 进行画板缩放之后,调整画板的位置
9 | ///
10 | internal class ScaleCenterConverter : IValueConverter
11 | {
12 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
13 | {
14 | if ((double)value > 0)
15 | {
16 | return (double)value / 2.0;
17 | }
18 |
19 | return 0;
20 | }
21 |
22 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
23 | {
24 | return null;
25 | }
26 | }
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Converter/SliderConverterToDouble.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Globalization;
3 | using System.Windows.Data;
4 |
5 | namespace GeometryTool;
6 |
7 | ///
8 | /// 线条粗细的转换
9 | ///
10 | internal class SliderConverterToDouble : IValueConverter
11 | {
12 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
13 | {
14 | if (value != null) //判断是否为空
15 | {
16 | var sliderValue = (double)value;
17 | if (sliderValue <= 1)
18 | {
19 | return sliderValue * 10;
20 | }
21 |
22 | return sliderValue;
23 | }
24 |
25 | return null;
26 | }
27 |
28 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
29 | {
30 | if (value != null) //判断是否为空
31 | {
32 | var sliderValue = (double)value;
33 | if (sliderValue < 1)
34 | {
35 | return sliderValue;
36 | }
37 |
38 | return System.Convert.ToDouble(sliderValue / 10);
39 | }
40 |
41 | return null;
42 | }
43 | }
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/GeometryTool.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net6.0-windows
4 | WinExe
5 | false
6 | true
7 | true
8 | true
9 |
10 |
11 | app.manifest
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Image/Bezier.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotnet-campus/GeometryToolDemo/b488b66099e0de4c96ca4a184e56d51084ffd2f8/src/GeometryTool/GeometryTool/Image/Bezier.png
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Image/Circle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotnet-campus/GeometryToolDemo/b488b66099e0de4c96ca4a184e56d51084ffd2f8/src/GeometryTool/GeometryTool/Image/Circle.png
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Image/Color.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotnet-campus/GeometryToolDemo/b488b66099e0de4c96ca4a184e56d51084ffd2f8/src/GeometryTool/GeometryTool/Image/Color.png
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Image/Curve.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotnet-campus/GeometryToolDemo/b488b66099e0de4c96ca4a184e56d51084ffd2f8/src/GeometryTool/GeometryTool/Image/Curve.png
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Image/Down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotnet-campus/GeometryToolDemo/b488b66099e0de4c96ca4a184e56d51084ffd2f8/src/GeometryTool/GeometryTool/Image/Down.png
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Image/DowntoUp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotnet-campus/GeometryToolDemo/b488b66099e0de4c96ca4a184e56d51084ffd2f8/src/GeometryTool/GeometryTool/Image/DowntoUp.png
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Image/Ellipse.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotnet-campus/GeometryToolDemo/b488b66099e0de4c96ca4a184e56d51084ffd2f8/src/GeometryTool/GeometryTool/Image/Ellipse.png
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Image/Left.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotnet-campus/GeometryToolDemo/b488b66099e0de4c96ca4a184e56d51084ffd2f8/src/GeometryTool/GeometryTool/Image/Left.png
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Image/Line.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotnet-campus/GeometryToolDemo/b488b66099e0de4c96ca4a184e56d51084ffd2f8/src/GeometryTool/GeometryTool/Image/Line.png
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Image/Pan.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotnet-campus/GeometryToolDemo/b488b66099e0de4c96ca4a184e56d51084ffd2f8/src/GeometryTool/GeometryTool/Image/Pan.png
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Image/QBezier.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotnet-campus/GeometryToolDemo/b488b66099e0de4c96ca4a184e56d51084ffd2f8/src/GeometryTool/GeometryTool/Image/QBezier.png
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Image/Rectangular.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotnet-campus/GeometryToolDemo/b488b66099e0de4c96ca4a184e56d51084ffd2f8/src/GeometryTool/GeometryTool/Image/Rectangular.png
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Image/Right.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotnet-campus/GeometryToolDemo/b488b66099e0de4c96ca4a184e56d51084ffd2f8/src/GeometryTool/GeometryTool/Image/Right.png
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Image/RighttoLeft.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotnet-campus/GeometryToolDemo/b488b66099e0de4c96ca4a184e56d51084ffd2f8/src/GeometryTool/GeometryTool/Image/RighttoLeft.png
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Image/Triangle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotnet-campus/GeometryToolDemo/b488b66099e0de4c96ca4a184e56d51084ffd2f8/src/GeometryTool/GeometryTool/Image/Triangle.png
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Image/TurnOver.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotnet-campus/GeometryToolDemo/b488b66099e0de4c96ca4a184e56d51084ffd2f8/src/GeometryTool/GeometryTool/Image/TurnOver.png
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Image/Up.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotnet-campus/GeometryToolDemo/b488b66099e0de4c96ca4a184e56d51084ffd2f8/src/GeometryTool/GeometryTool/Image/Up.png
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Image/UptoDown.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotnet-campus/GeometryToolDemo/b488b66099e0de4c96ca4a184e56d51084ffd2f8/src/GeometryTool/GeometryTool/Image/UptoDown.png
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Image/XuanZhuan.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotnet-campus/GeometryToolDemo/b488b66099e0de4c96ca4a184e56d51084ffd2f8/src/GeometryTool/GeometryTool/Image/XuanZhuan.png
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Image/cuxi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotnet-campus/GeometryToolDemo/b488b66099e0de4c96ca4a184e56d51084ffd2f8/src/GeometryTool/GeometryTool/Image/cuxi.png
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Image/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotnet-campus/GeometryToolDemo/b488b66099e0de4c96ca4a184e56d51084ffd2f8/src/GeometryTool/GeometryTool/Image/icon.png
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Image/lock.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotnet-campus/GeometryToolDemo/b488b66099e0de4c96ca4a184e56d51084ffd2f8/src/GeometryTool/GeometryTool/Image/lock.png
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Image/myCanvas.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotnet-campus/GeometryToolDemo/b488b66099e0de4c96ca4a184e56d51084ffd2f8/src/GeometryTool/GeometryTool/Image/myCanvas.png
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Image/point.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotnet-campus/GeometryToolDemo/b488b66099e0de4c96ca4a184e56d51084ffd2f8/src/GeometryTool/GeometryTool/Image/point.png
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Image/unlock.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dotnet-campus/GeometryToolDemo/b488b66099e0de4c96ca4a184e56d51084ffd2f8/src/GeometryTool/GeometryTool/Image/unlock.png
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Model/AutoPoints.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 |
3 | namespace GeometryTool;
4 |
5 | ///
6 | /// 坐标点的换算,实现自动吸附功能
7 | ///
8 | public class AutoPoints
9 | {
10 | ///
11 | /// 坐标点的换算,实现自动吸附功能
12 | ///
13 | public Point GetAutoAdsorbPoint(Point oldPoint)
14 | {
15 | var p = new Point();
16 | if (oldPoint.X * 10 % 10 >= 5)
17 | {
18 | var b = (int)oldPoint.X; //计算离其最近的一个X坐标
19 | p.X = (int)oldPoint.X + 1;
20 | }
21 | else
22 | {
23 | p.X = (int)oldPoint.X;
24 | }
25 |
26 | if (oldPoint.Y * 10 % 10 >= 5) //计算离其最近的一个Y坐标
27 | {
28 | p.Y = (int)oldPoint.Y + 1;
29 | }
30 | else
31 | {
32 | p.Y = (int)oldPoint.Y;
33 | }
34 |
35 | return p;
36 | }
37 | }
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Model/BorderWithAdorner.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Windows.Controls;
3 | using System.Windows.Documents;
4 | using System.Windows.Input;
5 | using System.Windows.Media;
6 | using System.Windows.Shapes;
7 |
8 | namespace GeometryTool;
9 |
10 | ///
11 | /// 包含着图形的border,主要是显示选择框
12 | ///
13 | public class BorderWithAdorner : Border
14 | {
15 | public List EllipseList { get; }
16 |
17 | ///
18 | /// 图形的选择框
19 | ///
20 | public GeometryAdorner GeometryAdorner { set; get; }
21 |
22 | ///
23 | /// 最大的X位置
24 | ///
25 | public double MaxX { set; get; }
26 |
27 | ///
28 | /// 最大的Y位置
29 | ///
30 | public double MaxY { set; get; }
31 |
32 | ///
33 | /// 最小的X位置
34 | ///
35 | public double MinX { set; get; }
36 |
37 | ///
38 | /// 最小的Y位置
39 | ///
40 | public double MinY { set; get; }
41 |
42 | public BorderWithAdorner()
43 | {
44 | MouseDown += Element_MouseDown;
45 | EllipseList = new List();
46 | }
47 |
48 | ///
49 | /// 当鼠标点击图形的的时候,产生边线
50 | ///
51 | ///
52 | ///
53 | public void Element_MouseDown(object sender, MouseEventArgs e)
54 | {
55 | if (MainWindow.ActionMode == "Select")
56 | {
57 | ShowAdorner();
58 | }
59 | }
60 |
61 | ///
62 | /// 展示Adorner
63 | ///
64 | public void ShowAdorner()
65 | {
66 | var layer = AdornerLayer.GetAdornerLayer(Parent as Canvas);
67 | if (layer != null)
68 | {
69 | GetFourPoint(this);
70 | if (GeometryAdorner == null)
71 | {
72 | var path = Child as Path;
73 | GeometryAdorner = new GeometryAdorner(this);
74 | layer.Add(GeometryAdorner);
75 | }
76 | }
77 | }
78 |
79 | ///
80 | /// 获取图形四个边角的位置
81 | ///
82 | public void GetFourPoint(BorderWithAdorner borderWithAdorner)
83 | {
84 | if (borderWithAdorner.EllipseList.Count > 0)
85 | {
86 | borderWithAdorner.MinX = (EllipseList[0].Data as EllipseGeometry).Center.X;
87 | borderWithAdorner.MinY = (EllipseList[0].Data as EllipseGeometry).Center.Y;
88 | borderWithAdorner.MaxX = 0;
89 | borderWithAdorner.MaxY = 0;
90 | }
91 |
92 | foreach (var path in borderWithAdorner.EllipseList)
93 | {
94 | var item = path.Data as EllipseGeometry;
95 | var point = item.Center;
96 | if (borderWithAdorner.MaxX < point.X)
97 | {
98 | borderWithAdorner.MaxX = point.X;
99 | }
100 |
101 | if (borderWithAdorner.MaxY < point.Y)
102 | {
103 | borderWithAdorner.MaxY = point.Y;
104 | }
105 |
106 | if (borderWithAdorner.MinX > point.X)
107 | {
108 | borderWithAdorner.MinX = point.X;
109 | }
110 |
111 | if (borderWithAdorner.MinY > point.Y)
112 | {
113 | borderWithAdorner.MinY = point.Y;
114 | }
115 | }
116 | }
117 |
118 | ///
119 | /// 用于复制Border,模仿深度复制
120 | ///
121 | ///
122 | ///
123 | public BorderWithAdorner CopyBorder(BorderWithAdorner borderWithAdorner)
124 | {
125 | var path = borderWithAdorner.Child as Path;
126 | var graphAppearance = new GraphAppearance
127 | {
128 | Stroke = path.Stroke,
129 | StrokeThickness = path.StrokeThickness,
130 | StrokeDashArray = path.StrokeDashArray,
131 | Fill = path.Fill
132 | };
133 | var geometryStringConverter = new GeometryStringConverter(MainWindow.MyRootCanvas, graphAppearance);
134 | var miniLanguage =
135 | geometryStringConverter.StringFromGeometry(borderWithAdorner.Child as Path); //把该图形转化成为Mini-Language
136 | var newBorderWithAdorner =
137 | geometryStringConverter.GeometryFromString(miniLanguage); //把Mini-Language转化成为图形,实现图形的深度复制
138 | var newPath = newBorderWithAdorner.Child as Path;
139 | newPath.Stroke = path.Stroke;
140 | newPath.StrokeThickness = path.StrokeThickness;
141 | newPath.StrokeDashArray = path.StrokeDashArray;
142 | newPath.Fill = path.Fill;
143 | return newBorderWithAdorner;
144 | }
145 | }
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Model/BorderWithDrag.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Windows;
3 | using System.Windows.Controls;
4 | using System.Windows.Data;
5 | using System.Windows.Input;
6 | using System.Windows.Media;
7 | using System.Windows.Shapes;
8 |
9 | namespace GeometryTool
10 | {
11 | ///
12 | /// 主要让border添加了拖动响应,用于点
13 | ///
14 | public class BorderWithDrag : Border
15 | {
16 | ///
17 | /// 依赖属性,用于表示该位置是否含有多个点,来控制Adorner的显示
18 | ///
19 | public static readonly DependencyProperty HasOtherPointProperty =
20 | DependencyProperty.Register("HasOtherPoint", typeof(bool), typeof(BorderWithDrag),
21 | new FrameworkPropertyMetadata(false, null));
22 |
23 | ///
24 | /// 表示同一图层中
25 | ///
26 | public BorderWithDrag BrotherBorder;
27 |
28 | private readonly List _borderWithDragList = new();
29 |
30 | ///
31 | /// 表示是否可以拖动该点
32 | ///
33 | private bool _canLock;
34 |
35 | ///
36 | /// 用于表示所在的图形中所拥有的点集
37 | ///
38 | private readonly List _ellipseList;
39 |
40 | ///
41 | /// 表示点所在的图形
42 | ///
43 | private readonly Path _geometryPath;
44 |
45 | ///
46 | /// 表示是否可以拖动
47 | ///
48 | private bool _isDragDropInEffect;
49 |
50 | ///
51 | /// 表示控件的装饰器
52 | ///
53 | public LockAdorner LockAdornor { set; get; }
54 |
55 | ///
56 | /// 表示这是图形中第几个点
57 | ///
58 | private int _number;
59 |
60 | public List PointList = new();
61 |
62 | ///
63 | /// 表示当前点的位置
64 | ///
65 | private Point _currentPoint;
66 |
67 | ///
68 | /// 无参数的构造函数,主要是为了给Border响应鼠标的事件
69 | ///
70 | public BorderWithDrag()
71 | {
72 | MouseLeftButtonDown += Element_MouseLeftButtonDown;
73 | MouseMove += Element_MouseMove;
74 | MouseLeftButtonUp += Element_MouseLeftButtonUp;
75 | }
76 |
77 | ///
78 | /// 有参数的构造函数,主要添加了点的删除功能
79 | ///
80 | ///
81 | ///
82 | ///
83 | public BorderWithDrag(Path path, int number, List ellipseList)
84 | {
85 | MouseLeftButtonDown += Element_MouseLeftButtonDown;
86 | MouseMove += Element_MouseMove;
87 | MouseLeftButtonUp += Element_MouseLeftButtonUp;
88 | _geometryPath = path;
89 | _number = number;
90 | _ellipseList = ellipseList;
91 |
92 | ContextMenu = new ContextMenu();
93 | var deleteItem = new MenuItem();
94 | deleteItem.Header = "删除";
95 | deleteItem.Click += DeletedItem_Click;
96 | ContextMenu.Items.Add(deleteItem);
97 | }
98 |
99 | public bool HasOtherPoint
100 | {
101 | get => (bool)GetValue(HasOtherPointProperty);
102 | set => SetValue(HasOtherPointProperty, value);
103 | }
104 |
105 | ///
106 | /// 拖动鼠标,移动控件
107 | ///
108 | ///
109 | ///
110 | public void Element_MouseMove(object sender, MouseEventArgs e)
111 | {
112 | //判断是否在选择模式下的操作以及是否可以拖动
113 | if (_isDragDropInEffect && MainWindow.ActionMode == "Select")
114 | {
115 | var oldPoint = e.GetPosition(MainWindow.MyRootCanvas); //获取目前鼠标的相对位置
116 | var point = new AutoPoints().GetAutoAdsorbPoint(oldPoint); //计算最近的网格的位置
117 | _currentPoint = point;
118 | var currentFrameworkElement = sender as FrameworkElement;
119 | var borderWithDrag = currentFrameworkElement as BorderWithDrag;
120 | var path = borderWithDrag.Child as Path;
121 | var ellipse = path.Data as EllipseGeometry;
122 |
123 | ellipse.Center = new Point { X = point.X, Y = point.Y };
124 | }
125 | }
126 |
127 | ///
128 | /// 鼠标左击控件,使控件可以被拖动
129 | ///
130 | ///
131 | ///
132 | public void Element_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
133 | {
134 | //判断是否在选择模式下的操作
135 | if (MainWindow.ActionMode == "Select")
136 | {
137 | var currentFrameworkElement = sender as FrameworkElement;
138 |
139 | //判断是否命中的是点
140 | if (((currentFrameworkElement as BorderWithDrag).Child as Path).Data as EllipseGeometry != null)
141 | {
142 | currentFrameworkElement.CaptureMouse();
143 | currentFrameworkElement.Cursor = Cursors.Hand;
144 | _isDragDropInEffect = true; //设置可以拖动
145 | _canLock = true;
146 | }
147 |
148 | e.Handled = true;
149 | }
150 | }
151 |
152 | ///
153 | /// 鼠标释放,使控件不能被拖动
154 | ///
155 | ///
156 | ///
157 | public void Element_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
158 | {
159 | //判断是否在选择模式下的操作以及是否可以拖动
160 | var currentFrameworkElement = sender as FrameworkElement;
161 | if (_isDragDropInEffect && MainWindow.ActionMode == "Select" && _canLock)
162 | {
163 | _currentPoint = e.GetPosition((UIElement)sender); //获取当前鼠标的位置
164 | _currentPoint = new AutoPoints().GetAutoAdsorbPoint(_currentPoint); //修正该位置
165 | var borderLock = new BorderLock(this); //进行点融合的判读
166 | borderLock.Lock(_currentPoint);
167 | }
168 |
169 | _isDragDropInEffect = false;
170 | currentFrameworkElement.ReleaseMouseCapture();
171 | _canLock = false;
172 | }
173 |
174 | ///
175 | /// 添加Border的删除功能
176 | ///
177 | ///
178 | ///
179 | public void DeletedItem_Click(object sender, RoutedEventArgs e)
180 | {
181 | _borderWithDragList.Clear();
182 | VisualTreeHelper.HitTest(Parent as Canvas, null, //进行命中测试
183 | MyHitTestResult,
184 | new PointHitTestParameters(_currentPoint));
185 | var deleteAll = false;
186 |
187 | foreach (var item in _borderWithDragList)
188 | {
189 | if (item.HasOtherPoint)
190 | {
191 | deleteAll = true;
192 | break;
193 | }
194 | }
195 |
196 | if (deleteAll)
197 | {
198 | foreach (var item in _borderWithDragList)
199 | {
200 | if (item._number != 0)
201 | {
202 | DeletePoint(item);
203 | }
204 | }
205 | }
206 | else
207 | {
208 | DeletePoint(this);
209 | }
210 | }
211 |
212 | ///
213 | /// 命中测试
214 | ///
215 | ///
216 | ///
217 | public HitTestResultBehavior MyHitTestResult(HitTestResult result)
218 | {
219 | var path = result.VisualHit as Path;
220 | if (path != null)
221 | {
222 | var border = path.Parent as BorderWithDrag;
223 | if (border != null) //如果命中的图形是BorderWithDrag,就Count++
224 | {
225 | _borderWithDragList.Add(border);
226 | }
227 | }
228 |
229 | return HitTestResultBehavior.Continue;
230 | }
231 |
232 | ///
233 | /// 融合点
234 | ///
235 | ///
236 | ///
237 | public void LockItem_Click(object sender, RoutedEventArgs e)
238 | {
239 | var borderLock = new BorderLock(this);
240 | borderLock.Lock(_currentPoint);
241 | HasOtherPoint = true;
242 | }
243 |
244 | ///
245 | /// 拆分点
246 | ///
247 | ///
248 | ///
249 | public void UnLockItem_Click(object sender, RoutedEventArgs e)
250 | {
251 | var borderLock = new BorderLock();
252 | borderLock.UnLock(this);
253 | HasOtherPoint = false;
254 | }
255 |
256 | ///
257 | /// 删除不必要的点
258 | ///
259 | ///
260 | public void DeletePoint(BorderWithDrag item)
261 | {
262 | if (item._number != 0)
263 | {
264 | var path = item._geometryPath.Data as PathGeometry; //获取该Border所在PathGeometry
265 | var pathFigure = path.Figures[0];
266 | if (item._number != 1) //如果要删除的不是起点
267 | {
268 | pathFigure.Segments.RemoveAt(item._number - 2); //直接移除Segment
269 | }
270 | else //如果要删除的是起点
271 | {
272 | var pathFigure2 = new PathFigure();
273 | for (var i = 0; i < pathFigure.Segments.Count; ++i)
274 | {
275 | if (i == 0) //复制出另外一个PathFigure,令其起点为原来的PathFigure中的LineSegment的Point
276 | {
277 | var binding = new Binding("Center")
278 | { Source = item._ellipseList[1].Data as EllipseGeometry };
279 | BindingOperations.SetBinding(pathFigure2, PathFigure.StartPointProperty, binding);
280 | }
281 | else
282 | {
283 | pathFigure2.Segments.Add(pathFigure.Segments[i]);
284 | }
285 | }
286 |
287 | path.Figures.Add(pathFigure2);
288 | path.Figures.RemoveAt(0);
289 | }
290 |
291 | MainWindow.MyRootCanvas.Children.Remove(item); //在窗体上移除该点
292 |
293 | item._ellipseList.RemoveAt(item._number - 1);
294 | for (var j = 0; j < item._ellipseList.Count; ++j) //重新定位点集的位置
295 | {
296 | var border = item._ellipseList[j].Parent as BorderWithDrag;
297 | border._number = j + 1;
298 | }
299 | }
300 | }
301 | }
302 | }
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Model/GeometryAdorner.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 | using System.Windows.Documents;
3 | using System.Windows.Media;
4 |
5 | namespace GeometryTool;
6 |
7 | ///
8 | /// 选择框的Adorner
9 | ///
10 | public class GeometryAdorner : Adorner
11 | {
12 | public GeometryChrome GeometryChrome { set; get; } //选择框的真正样式
13 | private readonly VisualCollection _visuals;
14 |
15 | ///
16 | /// 构造函数,主要是使chrome是DataContext为BorderWithAdorner
17 | ///
18 | ///
19 | public GeometryAdorner(BorderWithAdorner borderWithAdorner)
20 | : base(borderWithAdorner)
21 | {
22 | SnapsToDevicePixels = true;
23 | GeometryChrome = new GeometryChrome();
24 | GeometryChrome.DataContext = borderWithAdorner;
25 | _visuals = new VisualCollection(this);
26 | _visuals.Add(GeometryChrome);
27 | }
28 |
29 | ///
30 | /// 重写的VisualChildrenCount函数
31 | ///
32 | protected override int VisualChildrenCount => _visuals.Count;
33 |
34 | ///
35 | /// 重写的ArrangeOverride
36 | ///
37 | ///
38 | ///
39 | protected override Size ArrangeOverride(Size arrangeBounds)
40 | {
41 | arrangeBounds.Height += 0.5;
42 | arrangeBounds.Width += 0.5;
43 | GeometryChrome.Arrange(new Rect(arrangeBounds));
44 | return arrangeBounds;
45 | }
46 |
47 | ///
48 | /// 重写GetVisualChild函数
49 | ///
50 | ///
51 | ///
52 | protected override Visual GetVisualChild(int index)
53 | {
54 | return _visuals[index];
55 | }
56 | }
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Model/GeometryChrome.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 | using System.Windows.Controls;
3 | using System.Windows.Media;
4 | using System.Windows.Shapes;
5 |
6 | namespace GeometryTool;
7 |
8 | ///
9 | /// 图形的外层拖动的Adorner
10 | ///
11 | public class GeometryChrome : ContentControl
12 | {
13 | public GeometryChrome()
14 | {
15 | ContextMenu = new ContextMenu();
16 | var copyItem = new MenuItem();
17 | copyItem.Header = "复制";
18 | copyItem.Command = RoutedCommands.CopyCommand;
19 | ContextMenu.Items.Add(copyItem);
20 |
21 | var cutItem = new MenuItem();
22 | cutItem.Header = "剪切";
23 | cutItem.Command = RoutedCommands.CutCommand;
24 | ContextMenu.Items.Add(cutItem);
25 |
26 | var deleteItem = new MenuItem();
27 | deleteItem.Header = "删除";
28 | deleteItem.Command = RoutedCommands.DeleteCommand;
29 | ContextMenu.Items.Add(deleteItem);
30 |
31 | var topItem = new MenuItem();
32 | topItem.Header = "置于顶层";
33 | topItem.Click += TopItem_Click;
34 | ContextMenu.Items.Add(topItem);
35 | var bottomItem = new MenuItem();
36 |
37 | bottomItem.Header = "置于底层";
38 | bottomItem.Click += BottomItem_Click;
39 | ContextMenu.Items.Add(bottomItem);
40 | }
41 |
42 | public static void Arrange(GeometryChrome geometryChrome)
43 | {
44 | var border = geometryChrome.DataContext as BorderWithAdorner;
45 | geometryChrome.ArrangeOverride(new Size { Width = border.MaxX, Height = border.MaxY });
46 | }
47 |
48 | ///
49 | /// 绘制这个锁
50 | ///
51 | ///
52 | ///
53 | protected override Size ArrangeOverride(Size arrangeBounds)
54 | {
55 | var border = DataContext as BorderWithAdorner;
56 | border.MaxX = 0;
57 | border.MinX = (border.EllipseList[0].Data as EllipseGeometry).Center.X;
58 | border.MaxY = 0;
59 | border.MinY = (border.EllipseList[0].Data as EllipseGeometry).Center.Y;
60 | foreach (var path in border.EllipseList)
61 | {
62 | var item = path.Data as EllipseGeometry;
63 | var p = item.Center;
64 | if (border.MaxX < p.X)
65 | {
66 | border.MaxX = p.X;
67 | }
68 |
69 | if (border.MaxY < p.Y)
70 | {
71 | border.MaxY = p.Y;
72 | }
73 |
74 | if (border.MinX > p.X)
75 | {
76 | border.MinX = p.X;
77 | }
78 |
79 | if (border.MinY > p.Y)
80 | {
81 | border.MinY = p.Y;
82 | }
83 | }
84 |
85 | var pathGeometry = (border.Child as Path).Data as PathGeometry;
86 | if (pathGeometry.Figures[0].Segments.Count > 0)
87 | {
88 | var geometry = pathGeometry.GetFlattenedPathGeometry();
89 | var bound = geometry.GetRenderBounds(new Pen(null, (border.Child as Path).StrokeThickness));
90 | Width = bound.Width + 1;
91 | Height = bound.Height + 1;
92 | Margin = new Thickness(border.ActualWidth - bound.Width - 0.65, border.ActualHeight - bound.Height - 0.65,
93 | 0, 0);
94 | }
95 | else
96 | {
97 | Width = border.MaxX - border.MinX + 1;
98 | Height = border.MaxY - border.MinY + 1;
99 |
100 | Margin = new Thickness(border.MinX - 0.5, border.MinY - 0.5, 0, 0);
101 | }
102 |
103 | return base.ArrangeOverride(arrangeBounds);
104 | }
105 |
106 | ///
107 | /// 复制图形
108 | ///
109 | ///
110 | ///
111 | public void CopyItem_Click(object sender, RoutedEventArgs e)
112 | {
113 | var borderWithAdorner = DataContext as BorderWithAdorner;
114 | MainWindow.CopyBorderWithAdorner = borderWithAdorner; //记录要粘贴的图形
115 | MainWindow.PasteCount = 0; //把粘贴次数重置为0
116 |
117 | var rootCanvas = MainWindow.MyRootCanvas;
118 | var rootCanvasParent = rootCanvas.Parent as Canvas;
119 | var canvasBorder = rootCanvasParent.Parent as Border;
120 | var scrollViewer = canvasBorder.Parent as ScrollViewer;
121 | var rootGrid = scrollViewer.Parent as Grid;
122 | var mainWindow = rootGrid.Parent as MainWindow; //获取MainWindow实例,为了修改CanPaste属性
123 | mainWindow.CanPaste = true;
124 | }
125 |
126 | ///
127 | /// 剪切图形
128 | ///
129 | ///
130 | ///
131 | public void CutItem_Click(object sender, RoutedEventArgs e)
132 | {
133 | var borderWithAdorner = DataContext as BorderWithAdorner;
134 | MainWindow.CopyBorderWithAdorner = borderWithAdorner; //记录要粘贴的图形
135 | MainWindow.PasteCount = 0; //把粘贴次数重置为0
136 |
137 | var rootCanvas = MainWindow.MyRootCanvas;
138 | var rootCanvasParent = rootCanvas.Parent as Canvas;
139 | var canvasBorder = rootCanvasParent.Parent as Border;
140 | var scrollViewer = canvasBorder.Parent as ScrollViewer;
141 | var rootGrid = scrollViewer.Parent as Grid;
142 | var mainWindow = rootGrid.Parent as MainWindow; //获取MainWindow实例,为了修改CanPaste属性
143 | mainWindow.CanPaste = true;
144 | rootCanvas.Children.Remove(borderWithAdorner);
145 | foreach (var item in borderWithAdorner.EllipseList)
146 | {
147 | var borderWithDrag = item.Parent as BorderWithDrag;
148 | rootCanvas.Children.Remove(borderWithDrag);
149 | }
150 | }
151 |
152 | ///
153 | /// 删除图形
154 | ///
155 | ///
156 | ///
157 | public void DeleteItem_Click(object sender, RoutedEventArgs e)
158 | {
159 | var borderWithAdorner = DataContext as BorderWithAdorner;
160 | var rootCanvas = MainWindow.MyRootCanvas;
161 | rootCanvas.Children.Remove(borderWithAdorner); //移除图形
162 | foreach (var item in borderWithAdorner.EllipseList) //移除图形上面的点
163 | {
164 | var borderWithDrag = item.Parent as BorderWithDrag;
165 | rootCanvas.Children.Remove(borderWithDrag);
166 | }
167 | }
168 |
169 | ///
170 | /// 把图形至于顶层
171 | ///
172 | ///
173 | ///
174 | public void TopItem_Click(object sender, RoutedEventArgs e)
175 | {
176 | var borderWithAdorner = DataContext as BorderWithAdorner;
177 | Panel.SetZIndex(borderWithAdorner, MainWindow.HightestLevel++);
178 | for (var i = 0; i < borderWithAdorner.EllipseList.Count; ++i)
179 | {
180 | Panel.SetZIndex(borderWithAdorner.EllipseList[i].Parent as BorderWithDrag, MainWindow.HightestLevel);
181 | }
182 | }
183 |
184 | ///
185 | /// 把图形至于底层
186 | ///
187 | ///
188 | ///
189 | public void BottomItem_Click(object sender, RoutedEventArgs e)
190 | {
191 | var borderWithAdorner = DataContext as BorderWithAdorner;
192 | Panel.SetZIndex(borderWithAdorner, MainWindow.LowestLevel--);
193 | for (var i = 0; i < borderWithAdorner.EllipseList.Count; ++i)
194 | {
195 | Panel.SetZIndex(borderWithAdorner.EllipseList[i].Parent as BorderWithDrag, MainWindow.LowestLevel);
196 | }
197 | }
198 | }
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Model/GraphAppearance.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel;
2 | using System.Windows.Media;
3 |
4 | namespace GeometryTool;
5 |
6 | ///
7 | /// 图形的外观
8 | ///
9 | public class GraphAppearance
10 | {
11 | ///
12 | /// 填充规则
13 | ///
14 | private FillRule _fillRule;
15 |
16 | ///
17 | /// 设置虚实
18 | ///
19 | private DoubleCollection _strokeDashArray;
20 |
21 | ///
22 | /// 设置图形的线条的大小
23 | ///
24 | private double _strokeThickness;
25 |
26 | public GraphAppearance()
27 | {
28 | StrokeThickness = 0.1;
29 |
30 | Stroke = Brushes.Black;
31 | Fill = Brushes.Transparent;
32 | FillRule = FillRule.EvenOdd;
33 | StrokeDashArray = new DoubleCollection();
34 | StrokeDashArray.Add(1);
35 | StrokeDashArray.Add(0);
36 | }
37 |
38 | public double StrokeThickness
39 | {
40 | get => _strokeThickness;
41 | set
42 | {
43 | _strokeThickness = value;
44 | if (PropertyChanged != null)
45 | {
46 | PropertyChanged.Invoke(this, new PropertyChangedEventArgs("StrokeThickness"));
47 | }
48 | }
49 | }
50 |
51 | public DoubleCollection StrokeDashArray
52 | {
53 | get => _strokeDashArray;
54 | set
55 | {
56 | _strokeDashArray = value;
57 | if (PropertyChanged != null)
58 | {
59 | PropertyChanged.Invoke(this, new PropertyChangedEventArgs("StrokeDashArray"));
60 | }
61 | }
62 | }
63 |
64 | ///
65 | /// 设置图形的颜色
66 | ///
67 | private Brush _stroke;
68 |
69 | ///
70 | /// 设置图形的颜色
71 | ///
72 | public Brush Stroke
73 | {
74 | get => _stroke;
75 | set
76 | {
77 | _stroke = value;
78 | if (PropertyChanged != null)
79 | {
80 | PropertyChanged.Invoke(this, new PropertyChangedEventArgs("Stroke"));
81 | }
82 | }
83 | }
84 |
85 | ///
86 | /// 设置图形填充的颜色
87 | ///
88 | private Brush _fill;
89 |
90 | ///
91 | /// 设置图形填充的颜色
92 | ///
93 | public Brush Fill
94 | {
95 | get => _fill;
96 | set
97 | {
98 | _fill = value;
99 | if (PropertyChanged != null)
100 | {
101 | PropertyChanged.Invoke(this, new PropertyChangedEventArgs("Fill"));
102 | }
103 | }
104 | }
105 |
106 | public FillRule FillRule
107 | {
108 | get => _fillRule;
109 | set
110 | {
111 | _fillRule = value;
112 | if (PropertyChanged != null)
113 | {
114 | PropertyChanged.Invoke(this, new PropertyChangedEventArgs("FillRule"));
115 | }
116 | }
117 | }
118 |
119 | public event PropertyChangedEventHandler PropertyChanged;
120 | }
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Model/LockAdorner.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 | using System.Windows.Documents;
3 | using System.Windows.Media;
4 |
5 | namespace GeometryTool;
6 |
7 | ///
8 | /// 融合点时锁的Adorner
9 | ///
10 | public class LockAdorner : Adorner
11 | {
12 | ///
13 | /// 锁的样式
14 | ///
15 | public LockChrome LockChrome { get; }
16 | private readonly VisualCollection _visuals;
17 |
18 | ///
19 | /// 重写的构造函数
20 | ///
21 | ///
22 | public LockAdorner(UIElement borderWithAdorner)
23 | : base(borderWithAdorner)
24 | {
25 | SnapsToDevicePixels = true;
26 | LockChrome = new LockChrome();
27 | LockChrome.DataContext = borderWithAdorner;
28 | _visuals = new VisualCollection(this);
29 | _visuals.Add(LockChrome);
30 | }
31 |
32 | ///
33 | /// 重写的VisualChildrenCount
34 | ///
35 | protected override int VisualChildrenCount => _visuals.Count;
36 |
37 | ///
38 | /// 重写的ArrangeOverride函数
39 | ///
40 | ///
41 | ///
42 | protected override Size ArrangeOverride(Size arrangeBounds)
43 | {
44 | LockChrome.Arrange(new Rect(arrangeBounds));
45 | return arrangeBounds;
46 | }
47 |
48 | ///
49 | /// 重写的GetVisualChild的函数
50 | ///
51 | ///
52 | ///
53 | protected override Visual GetVisualChild(int index)
54 | {
55 | return _visuals[index];
56 | }
57 | }
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Model/LockChrome.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows;
3 | using System.Windows.Controls;
4 | using System.Windows.Data;
5 | using System.Windows.Input;
6 | using System.Windows.Media;
7 | using System.Windows.Media.Imaging;
8 |
9 | namespace GeometryTool;
10 |
11 | ///
12 | /// 锁真正的外观
13 | ///
14 | public class LockChrome : Image
15 | {
16 | private readonly bool _isLock;
17 |
18 | public LockChrome()
19 | {
20 | MouseDown += Element_MouseLeftButtonDown; //给这个Image添加点击事件,用于解开融合
21 | Source = new BitmapImage(new Uri("Image/lock.png", UriKind.Relative));
22 | _isLock = true;
23 | }
24 |
25 | ///
26 | /// 绘制这个锁
27 | ///
28 | ///
29 | ///
30 | protected override Size ArrangeOverride(Size arrangeBounds)
31 | {
32 | Width = 1.2;
33 | Height = 1.2;
34 |
35 | HorizontalAlignment = HorizontalAlignment.Right; //放在点的右下角
36 | VerticalAlignment = VerticalAlignment.Bottom;
37 |
38 | var translateTransform = new TranslateTransform(); //做一个旋转的变换
39 | translateTransform.X = 1.2;
40 | translateTransform.Y = -0.3;
41 | RenderTransform = translateTransform;
42 |
43 | var binding = new Binding("HasOtherPoint") { Converter = new ImageVisibilityConverter() };
44 | SetBinding(VisibilityProperty, binding); //当没有重合点的时候,隐藏锁
45 |
46 | return base.ArrangeOverride(arrangeBounds);
47 | }
48 |
49 | ///
50 | /// 用于解开融合
51 | ///
52 | ///
53 | ///
54 | public void Element_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
55 | {
56 | var chrome = sender as LockChrome;
57 | if (chrome != null)
58 | {
59 | if (_isLock) //融合变不融合
60 | {
61 | var border = DataContext as BorderWithDrag;
62 | var borderLock = new BorderLock();
63 | borderLock.UnLock(border);
64 | }
65 |
66 | e.Handled = true;
67 | }
68 | }
69 | }
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Model/MoveThumb.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 | using System.Windows.Controls.Primitives;
3 | using System.Windows.Media;
4 |
5 | namespace GeometryTool;
6 |
7 | ///
8 | /// 实现图形拖动的Thumb
9 | ///
10 | public class MoveThumb : Thumb
11 | {
12 | ///
13 | /// 包含该图形的BorderWithAdorner
14 | ///
15 | private BorderWithAdorner _borderWithAdorner;
16 |
17 | ///
18 | /// 构造函数,注册拖动事件
19 | ///
20 | public MoveThumb()
21 | {
22 | DragStarted += MoveThumb_DragStarted;
23 | DragDelta += MoveThumb_DragDelta;
24 | DragCompleted += MoveThumb_DragCompleted;
25 | }
26 |
27 | ///
28 | /// 开始拖动,主要是获取包含着被拖动的图形的BorderWithAdorner
29 | ///
30 | ///
31 | ///
32 | private void MoveThumb_DragStarted(object sender, DragStartedEventArgs e)
33 | {
34 | _borderWithAdorner = DataContext as BorderWithAdorner;
35 | }
36 |
37 | ///
38 | /// 图形被拖动过程
39 | ///
40 | ///
41 | ///
42 | private void MoveThumb_DragDelta(object sender, DragDeltaEventArgs e)
43 | {
44 | if (_borderWithAdorner != null)
45 | {
46 | var dragDelta = new Point(e.HorizontalChange, e.VerticalChange); //获取移动的距离
47 | var border = _borderWithAdorner;
48 | for (var i = 0; i < border.EllipseList.Count; ++i)
49 | {
50 | var borderWithDrag = border.EllipseList[i].Parent as BorderWithDrag;
51 | if (borderWithDrag.HasOtherPoint) //如果该点有锁,便跳过移动
52 | {
53 | continue;
54 | }
55 |
56 | var item = border.EllipseList[i].Data as EllipseGeometry; //更新点的坐标
57 | item.Center = new Point
58 | { X = item.Center.X + e.HorizontalChange, Y = item.Center.Y + e.VerticalChange };
59 | }
60 | }
61 | }
62 |
63 | ///
64 | /// 图形结束拖动,实现自动吸附
65 | ///
66 | ///
67 | ///
68 | private void MoveThumb_DragCompleted(object sender, DragCompletedEventArgs e)
69 | {
70 | for (var i = 0; i < _borderWithAdorner.EllipseList.Count; ++i)
71 | {
72 | var item = _borderWithAdorner.EllipseList[i].Data as EllipseGeometry;
73 | var p1 = item.Center;
74 | item.Center = new AutoPoints().GetAutoAdsorbPoint(p1);
75 | }
76 | }
77 | }
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Model/ResizeRotateAdorner.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 | using System.Windows.Controls;
3 | using System.Windows.Documents;
4 | using System.Windows.Media;
5 |
6 | namespace GeometryTool;
7 |
8 | ///
9 | /// 拖动边的Adorner
10 | ///
11 | public class ResizeRotateAdorner : Adorner
12 | {
13 | private readonly ResizeRotateChrome _chrome;
14 | private readonly VisualCollection _visuals;
15 |
16 | public ResizeRotateAdorner(ContentControl borderWithAdorner)
17 | : base(borderWithAdorner)
18 | {
19 | SnapsToDevicePixels = true;
20 | _chrome = new ResizeRotateChrome();
21 | _chrome.DataContext = borderWithAdorner;
22 | _visuals = new VisualCollection(this);
23 | _visuals.Add(_chrome);
24 | }
25 |
26 | protected override int VisualChildrenCount => _visuals.Count;
27 |
28 | protected override Size ArrangeOverride(Size arrangeBounds)
29 | {
30 | _chrome.Arrange(new Rect(arrangeBounds));
31 | return arrangeBounds;
32 | }
33 |
34 | protected override Visual GetVisualChild(int index)
35 | {
36 | return _visuals[index];
37 | }
38 | }
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Model/ResizeRotateChrome.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 | using System.Windows.Controls;
3 |
4 | namespace GeometryTool;
5 |
6 | ///
7 | /// 可拖动的选择框的边的外观
8 | ///
9 | public class ResizeRotateChrome : Control
10 | {
11 | static ResizeRotateChrome()
12 | {
13 | DefaultStyleKeyProperty.OverrideMetadata(typeof(ResizeRotateChrome),
14 | new FrameworkPropertyMetadata(typeof(ResizeRotateChrome)));
15 | }
16 | }
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Model/ResizeThumb.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows;
3 | using System.Windows.Controls.Primitives;
4 | using System.Windows.Media;
5 |
6 | namespace GeometryTool;
7 |
8 | ///
9 | /// 可拖动的选择框的边的外观
10 | ///
11 | public class ResizeThumb : Thumb
12 | {
13 | private BorderWithAdorner _borderWithAdorner;
14 |
15 | ///
16 | /// 构造函数,用于注册事件
17 | ///
18 | public ResizeThumb()
19 | {
20 | DragStarted += ResizeThumb_DragStarted;
21 | DragDelta += ResizeThumb_DragDelta;
22 | DragCompleted += ResizeThumb_DragCompleted;
23 | }
24 |
25 | ///
26 | /// 开始缩放
27 | ///
28 | ///
29 | ///
30 | private void ResizeThumb_DragStarted(object sender, DragStartedEventArgs e)
31 | {
32 | _borderWithAdorner = DataContext as BorderWithAdorner;
33 | }
34 |
35 | ///
36 | /// 图形的缩放
37 | ///
38 | ///
39 | ///
40 | private void ResizeThumb_DragDelta(object sender, DragDeltaEventArgs e)
41 | {
42 | double maxX = _borderWithAdorner.MaxX, maxY = _borderWithAdorner.MaxY; //记录图形四个角落的值
43 | double minX = _borderWithAdorner.MinX, minY = _borderWithAdorner.MinY;
44 | var oldHeight = _borderWithAdorner.ActualHeight; //记录图形未缩放之前的高度
45 | var oldWidth = _borderWithAdorner.ActualWidth; //记录图形未缩放之前的宽度
46 | double newWidth = 0; //缩放后图形新的宽度
47 | double newHeight = 0;
48 | double deltaVertical, deltaHorizontal = 0; //缩放后图形新的高度
49 | if (_borderWithAdorner != null)
50 | {
51 | switch (VerticalAlignment)
52 | {
53 | case VerticalAlignment.Bottom: //如果是拖动下面的边
54 | deltaVertical = -e.VerticalChange;
55 | newHeight = oldHeight - deltaVertical;
56 | break;
57 | case VerticalAlignment.Top: //如果是拖动上面的边
58 | deltaVertical = e.VerticalChange;
59 | newHeight = oldHeight - deltaVertical;
60 | break;
61 | }
62 |
63 | switch (HorizontalAlignment)
64 | {
65 | case HorizontalAlignment.Left: //如果是拖动左边的边
66 | deltaHorizontal = e.HorizontalChange;
67 | newWidth = oldWidth - deltaHorizontal;
68 |
69 | break;
70 | case HorizontalAlignment.Right: //如果是拖动右边的边
71 | deltaHorizontal = -e.HorizontalChange;
72 | newWidth = oldWidth - deltaHorizontal;
73 | break;
74 | }
75 | }
76 |
77 | var multipleX = newWidth / oldWidth;
78 | var multipleY = newHeight / oldHeight;
79 | switch (VerticalAlignment) //改变图形的高度
80 | {
81 | case VerticalAlignment.Bottom:
82 | for (var i = 0; i < _borderWithAdorner.EllipseList.Count; ++i)
83 | {
84 | var item = _borderWithAdorner.EllipseList[i].Data as EllipseGeometry;
85 | var borderWithDrag = _borderWithAdorner.EllipseList[i].Parent as BorderWithDrag;
86 | if (borderWithDrag.HasOtherPoint)
87 | {
88 | continue;
89 | }
90 |
91 | var p1 = item.Center;
92 | var newY = multipleY * (p1.Y - minY) + minY;
93 | item.Center = new Point { X = p1.X, Y = newY };
94 | }
95 |
96 | break;
97 | case VerticalAlignment.Top:
98 | for (var i = 0; i < _borderWithAdorner.EllipseList.Count; ++i)
99 | {
100 | var item = _borderWithAdorner.EllipseList[i].Data as EllipseGeometry;
101 | var borderWithDrag = _borderWithAdorner.EllipseList[i].Parent as BorderWithDrag;
102 | if (borderWithDrag.HasOtherPoint)
103 | {
104 | continue;
105 | }
106 |
107 | var p1 = item.Center;
108 | var newY = multipleY * (p1.Y - maxY) + maxY;
109 | item.Center = new Point { X = p1.X, Y = newY };
110 | }
111 |
112 | break;
113 | }
114 |
115 | switch (HorizontalAlignment) //改变图形的宽度
116 | {
117 | case HorizontalAlignment.Left:
118 | for (var i = 0; i < _borderWithAdorner.EllipseList.Count; ++i)
119 | {
120 | var item = _borderWithAdorner.EllipseList[i].Data as EllipseGeometry;
121 | var borderWithDrag = _borderWithAdorner.EllipseList[i].Parent as BorderWithDrag;
122 | if (borderWithDrag.HasOtherPoint)
123 | {
124 | continue;
125 | }
126 |
127 | var p1 = item.Center;
128 | var newX = multipleX * (p1.X - maxX) + maxX;
129 | item.Center = new Point { X = newX, Y = p1.Y };
130 | if (i < 2)
131 | {
132 | Console.WriteLine("mouse: " + e.HorizontalChange + " p" + i + ": " + newX + " ");
133 | }
134 | }
135 |
136 | break;
137 | case HorizontalAlignment.Right:
138 | for (var i = 0; i < _borderWithAdorner.EllipseList.Count; ++i)
139 | {
140 | var item = _borderWithAdorner.EllipseList[i].Data as EllipseGeometry;
141 | var borderWithDrag = _borderWithAdorner.EllipseList[i].Parent as BorderWithDrag;
142 | if (borderWithDrag.HasOtherPoint)
143 | {
144 | continue;
145 | }
146 |
147 | var p1 = item.Center;
148 | var newX = multipleX * (p1.X - minX) + minX;
149 |
150 | item.Center = new Point { X = newX, Y = p1.Y };
151 | }
152 |
153 | break;
154 | }
155 |
156 | _borderWithAdorner.MinX = _borderWithAdorner.MaxX;
157 | _borderWithAdorner.MaxX = 0;
158 | _borderWithAdorner.MinY = _borderWithAdorner.MaxY;
159 |
160 | _borderWithAdorner.MaxY = 0;
161 |
162 | foreach (var path in _borderWithAdorner.EllipseList) //重新计算图形的四个边角值
163 | {
164 | var item = path.Data as EllipseGeometry;
165 | var p = item.Center;
166 | if (_borderWithAdorner.MaxX < p.X)
167 | {
168 | _borderWithAdorner.MaxX = p.X;
169 | }
170 |
171 | if (_borderWithAdorner.MaxY < p.Y)
172 | {
173 | _borderWithAdorner.MaxY = p.Y;
174 | }
175 |
176 | if (_borderWithAdorner.MinX > p.X)
177 | {
178 | _borderWithAdorner.MinX = p.X;
179 | }
180 |
181 | if (_borderWithAdorner.MinY > p.Y)
182 | {
183 | _borderWithAdorner.MinY = p.Y;
184 | }
185 | }
186 |
187 | GeometryChrome.Arrange(_borderWithAdorner.GeometryAdorner.GeometryChrome);
188 | e.Handled = true;
189 | }
190 |
191 | ///
192 | /// 缩放后自动吸附
193 | ///
194 | ///
195 | ///
196 | private void ResizeThumb_DragCompleted(object sender, DragCompletedEventArgs e)
197 | {
198 | for (var i = 0; i < _borderWithAdorner.EllipseList.Count; ++i)
199 | {
200 | var item = _borderWithAdorner.EllipseList[i].Data as EllipseGeometry;
201 | var p1 = item.Center;
202 | item.Center = new AutoPoints().GetAutoAdsorbPoint(p1);
203 | }
204 |
205 | e.Handled = true;
206 | }
207 | }
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Model/RotateThumb.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows;
3 | using System.Windows.Controls;
4 | using System.Windows.Controls.Primitives;
5 | using System.Windows.Input;
6 | using System.Windows.Media;
7 |
8 | namespace GeometryTool;
9 |
10 | ///
11 | /// 图形旋转的Thumb
12 | ///
13 | public class RotateThumb : Thumb
14 | {
15 | ///
16 | /// 图形的Border
17 | ///
18 | private BorderWithAdorner _borderWithAdorner;
19 |
20 | ///
21 | /// 图形所在的画布
22 | ///
23 | private Canvas _canvas;
24 |
25 | ///
26 | /// 图形的重点
27 | ///
28 | private Point _centerPoint;
29 |
30 | ///
31 | /// 开始的坐标向量
32 | ///
33 | private Vector _startVector;
34 |
35 | public RotateThumb()
36 | {
37 | DragDelta += RotateThumb_DragDelta;
38 | DragStarted += RotateThumb_DragStarted;
39 | DragCompleted += RotateThumb_DragCompleted;
40 | }
41 |
42 | ///
43 | /// 开始旋转
44 | ///
45 | ///
46 | ///
47 | private void RotateThumb_DragStarted(object sender, DragStartedEventArgs e)
48 | {
49 | _borderWithAdorner = DataContext as BorderWithAdorner;
50 |
51 | if (_borderWithAdorner != null)
52 | {
53 | _canvas = VisualTreeHelper.GetParent(_borderWithAdorner) as Canvas;
54 | if (_canvas != null)
55 | {
56 | _centerPoint = _borderWithAdorner.TranslatePoint(
57 | new Point
58 | {
59 | X = (_borderWithAdorner.MaxX + _borderWithAdorner.MinX) / 2.0,
60 | Y = (_borderWithAdorner.MaxY + _borderWithAdorner.MinY) / 2.0
61 | },
62 | _canvas);
63 |
64 | var startPoint = Mouse.GetPosition(_canvas);
65 | _startVector = Point.Subtract(startPoint, _centerPoint); //计算开始的向量
66 | }
67 | }
68 | }
69 |
70 | ///
71 | /// 旋转的过程
72 | ///
73 | ///
74 | ///
75 | private void RotateThumb_DragDelta(object sender, DragDeltaEventArgs e)
76 | {
77 | if (_borderWithAdorner != null && _canvas != null)
78 | {
79 | var currentPoint = Mouse.GetPosition(_canvas);
80 | var deltaVector = Point.Subtract(currentPoint, _centerPoint);
81 | var angle = Vector.AngleBetween(_startVector, deltaVector) / 360 * 2 * Math.PI;
82 | var centerX = (_borderWithAdorner.MaxX + _borderWithAdorner.MinX) / 2.0;
83 | var centerY = (_borderWithAdorner.MaxY + _borderWithAdorner.MinY) / 2.0; //计算旋转的角度,中点坐标
84 |
85 | foreach (var item in _borderWithAdorner.EllipseList) //根据公式来计算算旋转后的位置
86 | {
87 | if ((item.Parent as BorderWithDrag).HasOtherPoint)
88 | {
89 | continue;
90 | }
91 |
92 | var ellipse = item.Data as EllipseGeometry;
93 | var oldPoint = ellipse.Center;
94 | var newX = (oldPoint.X - centerX) * Math.Cos(angle) - (oldPoint.Y - centerY) * Math.Sin(angle) +
95 | centerX;
96 | var newY = (oldPoint.X - centerX) * Math.Sin(angle) + (oldPoint.Y - centerY) * Math.Cos(angle) +
97 | centerY;
98 | ellipse.Center = new Point { X = newX, Y = newY };
99 | }
100 |
101 | var startPoint = currentPoint;
102 | _startVector = Point.Subtract(startPoint, _centerPoint); //重新赋值开始的向量
103 | }
104 | }
105 |
106 | ///
107 | /// 结束旋转,并自动吸附到最近的点
108 | ///
109 | ///
110 | ///
111 | private void RotateThumb_DragCompleted(object sender, DragCompletedEventArgs e)
112 | {
113 | foreach (var item in _borderWithAdorner.EllipseList)
114 | {
115 | var ellipse = item.Data as EllipseGeometry;
116 | var oldPoint = ellipse.Center;
117 | var p = new AutoPoints().GetAutoAdsorbPoint(oldPoint);
118 | ellipse.Center = new Point { X = p.X, Y = p.Y };
119 | }
120 | }
121 | }
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Model/RoutedCommand.cs:
--------------------------------------------------------------------------------
1 | using System.Windows.Input;
2 |
3 | namespace GeometryTool;
4 |
5 | ///
6 | /// 定义快捷键的路由事件
7 | ///
8 | public static class RoutedCommands
9 | {
10 | ///
11 | /// 粘贴的路由事件
12 | ///
13 | private static RoutedUICommand _pasteCommand;
14 |
15 | ///
16 | /// 复制的路由事件
17 | ///
18 | private static RoutedUICommand _copyCommand;
19 |
20 | ///
21 | /// 剪切的路由事件
22 | ///
23 | private static RoutedUICommand _cutCommand;
24 |
25 | ///
26 | /// 删除的路由事件
27 | ///
28 | private static RoutedUICommand _deleteCommand;
29 |
30 | ///
31 | /// 保存的路由事件
32 | ///
33 | private static RoutedUICommand _saveCommand;
34 |
35 | ///
36 | /// 新建的路由事件
37 | ///
38 | private static RoutedUICommand _newCommand;
39 |
40 | ///
41 | /// 打开的路由事件
42 | ///
43 | private static RoutedUICommand _openCommand;
44 |
45 | public static RoutedUICommand PasteCommand
46 | {
47 | get
48 | {
49 | if (_pasteCommand == null)
50 | {
51 | _pasteCommand = new RoutedUICommand
52 | (
53 | "Paste",
54 | "Paste",
55 | typeof(MainWindow),
56 | new InputGestureCollection { new KeyGesture(Key.V, ModifierKeys.Control) }
57 | );
58 | }
59 |
60 | return _pasteCommand;
61 | }
62 | }
63 |
64 | public static RoutedUICommand CopyCommand
65 | {
66 | get
67 | {
68 | if (_copyCommand == null)
69 | {
70 | _copyCommand = new RoutedUICommand
71 | (
72 | "Copy",
73 | "Copy",
74 | typeof(MainWindow),
75 | new InputGestureCollection { new KeyGesture(Key.C, ModifierKeys.Control) }
76 | );
77 | }
78 |
79 | return _copyCommand;
80 | }
81 | }
82 |
83 | public static RoutedUICommand CutCommand
84 | {
85 | get
86 | {
87 | if (_cutCommand == null)
88 | {
89 | _cutCommand = new RoutedUICommand
90 | (
91 | "Cut",
92 | "Cut",
93 | typeof(MainWindow),
94 | new InputGestureCollection { new KeyGesture(Key.X, ModifierKeys.Control) }
95 | );
96 | }
97 |
98 | return _cutCommand;
99 | }
100 | }
101 |
102 | public static RoutedUICommand DeleteCommand
103 | {
104 | get
105 | {
106 | if (_deleteCommand == null)
107 | {
108 | _deleteCommand = new RoutedUICommand
109 | (
110 | "Delete",
111 | "Delete",
112 | typeof(MainWindow),
113 | new InputGestureCollection { new KeyGesture(Key.Delete) }
114 | );
115 | }
116 |
117 | return _deleteCommand;
118 | }
119 | }
120 |
121 | public static RoutedUICommand SaveCommand
122 | {
123 | get
124 | {
125 | if (_saveCommand == null)
126 | {
127 | _saveCommand = new RoutedUICommand
128 | (
129 | "Save",
130 | "Save",
131 | typeof(MainWindow),
132 | new InputGestureCollection { new KeyGesture(Key.S, ModifierKeys.Control) }
133 | );
134 | }
135 |
136 | return _saveCommand;
137 | }
138 | }
139 |
140 | public static RoutedUICommand NewCommand
141 | {
142 | get
143 | {
144 | if (_newCommand == null)
145 | {
146 | _newCommand = new RoutedUICommand
147 | (
148 | "New",
149 | "New",
150 | typeof(MainWindow),
151 | new InputGestureCollection { new KeyGesture(Key.N, ModifierKeys.Control) }
152 | );
153 | }
154 |
155 | return _newCommand;
156 | }
157 | }
158 |
159 | public static RoutedUICommand OpenCommand
160 | {
161 | get
162 | {
163 | if (_openCommand == null)
164 | {
165 | _openCommand = new RoutedUICommand
166 | (
167 | "Open",
168 | "Open",
169 | typeof(MainWindow),
170 | new InputGestureCollection { new KeyGesture(Key.O, ModifierKeys.Control) }
171 | );
172 | }
173 |
174 | return _openCommand;
175 | }
176 | }
177 | }
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Model/SizeAdorner.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 | using System.Windows.Documents;
3 | using System.Windows.Media;
4 |
5 | namespace GeometryTool;
6 |
7 | public class SizeAdorner : Adorner
8 | {
9 | private readonly SizeChrome _chrome;
10 | private readonly VisualCollection _visuals;
11 |
12 | public SizeAdorner(BorderWithAdorner borderWithAdorner)
13 | : base(borderWithAdorner)
14 | {
15 | SnapsToDevicePixels = true;
16 | _chrome = new SizeChrome();
17 | _chrome.DataContext = borderWithAdorner;
18 | _visuals = new VisualCollection(this);
19 | _visuals.Add(_chrome);
20 | }
21 |
22 | protected override int VisualChildrenCount => _visuals.Count;
23 |
24 | protected override Visual GetVisualChild(int index)
25 | {
26 | return _visuals[index];
27 | }
28 |
29 | protected override Size ArrangeOverride(Size arrangeBounds)
30 | {
31 | _chrome.Arrange(new Rect(new Point(0.0, 0.0), arrangeBounds));
32 | return arrangeBounds;
33 | }
34 | }
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Model/SizeChrome.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Globalization;
3 | using System.Windows;
4 | using System.Windows.Controls;
5 | using System.Windows.Data;
6 |
7 | namespace GeometryTool;
8 |
9 | public class SizeChrome : Control
10 | {
11 | static SizeChrome()
12 | {
13 | DefaultStyleKeyProperty.OverrideMetadata(typeof(SizeChrome), new FrameworkPropertyMetadata(typeof(SizeChrome)));
14 | }
15 | }
16 |
17 | public class DoubleFormatConverter : IValueConverter
18 | {
19 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
20 | {
21 | var d = (double)value;
22 | return Math.Round(d);
23 | }
24 |
25 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
26 | {
27 | return null;
28 | }
29 | }
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 | using System.Windows;
4 |
5 | // 关于程序集的一般信息通过以下
6 | // 属性集来控制。请更改这些属性值以修改
7 | // 与程序集关联的信息。
8 | [assembly: AssemblyTitle("GeometryTool")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("GeometryTool")]
13 | [assembly: AssemblyCopyright("Copyright © 2015")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // 如果将 ComVisible 设置为 False,则此程序集中的类型
18 | // 对于 COM 组件不可见。如果需要从 COM 访问此程序集中的
19 | // 类型,请针对该类型将 ComVisible 属性设置为 True。
20 | [assembly: ComVisible(false)]
21 |
22 | //为了开始生成可本地化的应用程序,请
23 | //在 .csproj 文件中 内
24 | //设置 CultureYouAreCodingWith。例如,如果您在源
25 | //文件中使用美国英语,请将 设置为 "en-US"。然后取消注释
26 | //以下 NeutralResourceLanguage 属性。请更新以下行中的 "en-US" 以
27 | //与项目文件中的 UICulture 设置匹配。
28 |
29 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
30 |
31 |
32 | [assembly: ThemeInfo(
33 | ResourceDictionaryLocation.None, //主题特定资源字典的位置
34 | //(在页面或应用程序资源字典中
35 | // 未找到资源的情况下使用)
36 | ResourceDictionaryLocation.SourceAssembly //普通资源字典的位置
37 | //(在页面、应用程序或任何主题特定资源字典中
38 | // 未找到资源的情况下使用)
39 | )]
40 |
41 |
42 | // 程序集的版本信息包含以下四个值:
43 | //
44 | // 主要版本
45 | // 次要版本
46 | // 内部版本号
47 | // 修订
48 | //
49 | // 您可以指定所有值,或者可以默认修订号和内部版本号,
50 | // 方法是按如下方式使用 "*":
51 | // [assembly: AssemblyVersion("1.0.*")]
52 | [assembly: AssemblyVersion("1.0.0.0")]
53 | [assembly: AssemblyFileVersion("1.0.0.0")]
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.34014
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace GeometryTool.Properties {
12 |
13 |
14 | ///
15 | /// A strongly-typed resource class, for looking up localized strings, etc.
16 | ///
17 | // This class was auto-generated by the StronglyTypedResourceBuilder
18 | // class via a tool like ResGen or Visual Studio.
19 | // To add or remove a member, edit your .ResX file then rerun ResGen
20 | // with the /str option, or rebuild your VS project.
21 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
22 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
23 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
24 | internal class Resources {
25 |
26 | private static global::System.Resources.ResourceManager resourceMan;
27 |
28 | private static global::System.Globalization.CultureInfo resourceCulture;
29 |
30 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
31 | internal Resources() {
32 | }
33 |
34 | ///
35 | /// Returns the cached ResourceManager instance used by this class.
36 | ///
37 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
38 | internal static global::System.Resources.ResourceManager ResourceManager {
39 | get {
40 | if ((resourceMan == null)) {
41 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("GeometryTool.Properties.Resources", typeof(Resources).Assembly);
42 | resourceMan = temp;
43 | }
44 | return resourceMan;
45 | }
46 | }
47 |
48 | ///
49 | /// Overrides the current thread's CurrentUICulture property for all
50 | /// resource lookups using this strongly typed resource class.
51 | ///
52 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
53 | internal static global::System.Globalization.CultureInfo Culture {
54 | get {
55 | return resourceCulture;
56 | }
57 | set {
58 | resourceCulture = value;
59 | }
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 | text/microsoft-resx
107 |
108 |
109 | 2.0
110 |
111 |
112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
113 |
114 |
115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Properties/Settings.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.34014
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace GeometryTool.Properties
12 | {
13 |
14 |
15 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
16 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
17 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
18 | {
19 |
20 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
21 |
22 | public static Settings Default
23 | {
24 | get
25 | {
26 | return defaultInstance;
27 | }
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Resources/ButtonImageStyle.xaml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
37 |
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Resources/ColorsStyle.xaml:
--------------------------------------------------------------------------------
1 |
3 |
13 |
14 |
15 |
16 |
17 |
27 |
28 |
29 |
30 |
31 |
56 |
77 |
98 |
131 |
132 |
133 |
134 |
135 |
167 |
168 |
169 |
170 |
171 |
172 |
206 |
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Resources/GeometryChromeStyle.xaml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
33 |
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Resources/LableStyle.xaml:
--------------------------------------------------------------------------------
1 |
3 |
7 |
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Resources/PulseButtonStyle.xaml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
39 |
40 |
41 |
42 |
43 |
45 |
46 |
47 |
48 |
49 |
51 |
52 |
53 |
54 |
55 |
56 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Resources/RadioButtonStyle.xaml:
--------------------------------------------------------------------------------
1 |
3 |
79 |
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Resources/ResizeRotateChromeStyle.xaml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
19 |
20 |
27 |
28 |
102 |
103 |
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Resources/RightTabControlStyle.xaml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
83 |
84 |
85 |
86 |
113 |
114 |
141 |
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/Resources/SizeChrome.xaml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
49 |
--------------------------------------------------------------------------------
/src/GeometryTool/GeometryTool/app.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
9 |
10 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------