Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
ImportedProjects
SVG
Commits
13132300
Commit
13132300
authored
10 years ago
by
Tebjan Halm
Browse files
Options
Download
Plain Diff
Merge pull request #97 from articulate/BoundsPerformance
Performance Improvement
parents
a653b4a4
1b8cb43f
Changes
27
Hide whitespace changes
Inline
Side-by-side
Showing
20 changed files
Source/Basic Shapes/SvgCircle.cs
+3
-3
Source/Basic Shapes/SvgCircle.cs
Source/Basic Shapes/SvgEllipse.cs
+3
-3
Source/Basic Shapes/SvgEllipse.cs
Source/Basic Shapes/SvgImage.cs
+15
-15
Source/Basic Shapes/SvgImage.cs
Source/Basic Shapes/SvgLine.cs
+2
-2
Source/Basic Shapes/SvgLine.cs
Source/Basic Shapes/SvgPolygon.cs
+2
-2
Source/Basic Shapes/SvgPolygon.cs
Source/Basic Shapes/SvgRectangle.cs
+3
-3
Source/Basic Shapes/SvgRectangle.cs
Source/Basic Shapes/SvgVisualElement.cs
+2
-18
Source/Basic Shapes/SvgVisualElement.cs
Source/DataTypes/SvgUnit.cs
+3
-3
Source/DataTypes/SvgUnit.cs
Source/Document Structure/SvgFragment.cs
+15
-37
Source/Document Structure/SvgFragment.cs
Source/Document Structure/SvgGroup.cs
+18
-21
Source/Document Structure/SvgGroup.cs
Source/Document Structure/SvgSwitch.cs
+17
-20
Source/Document Structure/SvgSwitch.cs
Source/Document Structure/SvgSymbol.cs
+16
-19
Source/Document Structure/SvgSymbol.cs
Source/Document Structure/SvgUse.cs
+3
-2
Source/Document Structure/SvgUse.cs
Source/Extensibility/SvgForeignObject.cs
+17
-20
Source/Extensibility/SvgForeignObject.cs
Source/Painting/GenericBoundable.cs
+2
-12
Source/Painting/GenericBoundable.cs
Source/Painting/ISvgBoundable.cs
+1
-14
Source/Painting/ISvgBoundable.cs
Source/Painting/ImmutableBoundable.cs
+19
-0
Source/Painting/ImmutableBoundable.cs
Source/Painting/SvgGradientServer.cs
+1
-1
Source/Painting/SvgGradientServer.cs
Source/Painting/SvgLinearGradientServer.cs
+2
-2
Source/Painting/SvgLinearGradientServer.cs
Source/Painting/SvgMarker.cs
+5
-8
Source/Painting/SvgMarker.cs
with
149 additions
and
205 deletions
+149
-205
Source/Basic Shapes/SvgCircle.cs
+
3
-
3
View file @
13132300
...
...
@@ -78,10 +78,10 @@ namespace Svg
/// <summary>
/// Gets the bounds of the circle.
/// </summary>
/// <
value
>The rectangular bounds of the circle.</
value
>
public
override
RectangleF
Bounds
/// <
returns
>The rectangular bounds of the circle.</
returns
>
public
override
RectangleF
Calculate
Bounds
()
{
get
{
return
this
.
Path
(
null
).
GetBounds
();
}
return
this
.
Path
(
null
).
GetBounds
();
}
/// <summary>
...
...
This diff is collapsed.
Click to expand it.
Source/Basic Shapes/SvgEllipse.cs
+
3
-
3
View file @
13132300
...
...
@@ -92,10 +92,10 @@ namespace Svg
/// <summary>
/// Gets the bounds of the element.
/// </summary>
/// <
value
>The bounds.</
value
>
public
override
RectangleF
Bounds
/// <
returns
>The bounds.</
returns
>
public
override
RectangleF
Calculate
Bounds
()
{
get
{
return
this
.
Path
(
null
).
GetBounds
();
}
return
this
.
Path
(
null
).
GetBounds
();
}
/// <summary>
...
...
This diff is collapsed.
Click to expand it.
Source/Basic Shapes/SvgImage.cs
+
15
-
15
View file @
13132300
...
...
@@ -76,21 +76,21 @@ namespace Svg
{
get
{
return
this
.
Attributes
.
GetAttribute
<
Uri
>(
"href"
);
}
set
{
this
.
Attributes
[
"href"
]
=
value
;
}
}
/// <summary>
/// Gets the bounds of the element.
/// </summary>
/// <
value
>The bounds.</
value>
public
override
RectangleF
Bounds
{
get
{
return
new
RectangleF
(
this
.
Location
.
ToDeviceValue
(
null
,
this
),
new
SizeF
(
this
.
Width
.
ToDeviceValue
(
null
,
UnitRenderingType
.
Horizontal
,
this
),
this
.
Height
.
ToDeviceValue
(
null
,
UnitRenderingType
.
Vertical
,
this
)));
}
}
}
/// <summary>
/// Gets the bounds of the element.
/// </summary>
/// <
returns
>The bounds.</
returns>
public
override
RectangleF
Calculate
Bounds
()
{
return
new
RectangleF
(
this
.
Location
.
ToDeviceValue
(
null
,
this
),
new
SizeF
(
this
.
Width
.
ToDeviceValue
(
null
,
UnitRenderingType
.
Horizontal
,
this
),
this
.
Height
.
ToDeviceValue
(
null
,
UnitRenderingType
.
Vertical
,
this
)));
}
/// <summary>
/// Gets the <see cref="GraphicsPath"/> for this element.
/// </summary>
...
...
This diff is collapsed.
Click to expand it.
Source/Basic Shapes/SvgLine.cs
+
2
-
2
View file @
13132300
...
...
@@ -171,9 +171,9 @@ namespace Svg
return
result
;
}
public
override
System
.
Drawing
.
RectangleF
Bounds
public
override
RectangleF
Calculate
Bounds
()
{
get
{
return
this
.
Path
(
null
).
GetBounds
();
}
return
this
.
Path
(
null
).
GetBounds
();
}
public
override
SvgElement
DeepCopy
()
...
...
This diff is collapsed.
Click to expand it.
Source/Basic Shapes/SvgPolygon.cs
+
2
-
2
View file @
13132300
...
...
@@ -130,9 +130,9 @@ namespace Svg
return
result
;
}
public
override
RectangleF
Bounds
public
override
RectangleF
Calculate
Bounds
()
{
get
{
return
this
.
Path
(
null
).
GetBounds
();
}
return
this
.
Path
(
null
).
GetBounds
();
}
...
...
This diff is collapsed.
Click to expand it.
Source/Basic Shapes/SvgRectangle.cs
+
3
-
3
View file @
13132300
...
...
@@ -165,10 +165,10 @@ namespace Svg
/// <summary>
/// Gets the bounds of the element.
/// </summary>
/// <
value
>The bounds.</
value
>
public
override
RectangleF
Bounds
/// <
returns
>The bounds.</
returns
>
public
override
RectangleF
Calculate
Bounds
()
{
get
{
return
Path
(
null
).
GetBounds
();
}
return
Path
(
null
).
GetBounds
();
}
/// <summary>
...
...
This diff is collapsed.
Click to expand it.
Source/Basic Shapes/SvgVisualElement.cs
+
2
-
18
View file @
13132300
...
...
@@ -19,27 +19,11 @@ namespace Svg
/// </summary>
public
abstract
GraphicsPath
Path
(
ISvgRenderer
renderer
);
PointF
ISvgBoundable
.
Location
{
get
{
return
Bounds
.
Location
;
}
}
SizeF
ISvgBoundable
.
Size
{
get
{
return
Bounds
.
Size
;
}
}
/// <summary>
/// Gets the bounds of the element.
/// </summary>
/// <
value
>The bounds.</
value
>
public
abstract
RectangleF
Bounds
{
get
;
}
/// <
returns
>The bounds.</
returns
>
public
abstract
RectangleF
CalculateBounds
();
/// <summary>
/// Gets the associated <see cref="SvgClipPath"/> if one has been specified.
...
...
This diff is collapsed.
Click to expand it.
Source/DataTypes/SvgUnit.cs
+
3
-
3
View file @
13132300
...
...
@@ -149,7 +149,7 @@ namespace Svg
break
;
}
System
.
Drawing
.
SizeF
size
=
boundable
.
Bounds
.
Size
;
System
.
Drawing
.
SizeF
size
=
boundable
.
Calculate
Bounds
()
.
Size
;
switch
(
renderType
)
{
...
...
@@ -157,13 +157,13 @@ namespace Svg
_deviceValue
=
(
size
.
Width
/
100
)
*
value
;
break
;
case
UnitRenderingType
.
HorizontalOffset
:
_deviceValue
=
(
size
.
Width
/
100
)
*
value
+
boundable
.
Location
.
X
;
_deviceValue
=
(
size
.
Width
/
100
)
*
value
+
boundable
.
CalculateBounds
().
Location
.
X
;
break
;
case
UnitRenderingType
.
Vertical
:
_deviceValue
=
(
size
.
Height
/
100
)
*
value
;
break
;
case
UnitRenderingType
.
VerticalOffset
:
_deviceValue
=
(
size
.
Height
/
100
)
*
value
+
boundable
.
Location
.
Y
;
_deviceValue
=
(
size
.
Height
/
100
)
*
value
+
boundable
.
CalculateBounds
().
Location
.
Y
;
break
;
default
:
_deviceValue
=
(
float
)(
Math
.
Sqrt
(
Math
.
Pow
(
size
.
Width
,
2
)
+
Math
.
Pow
(
size
.
Height
,
2
))
/
Math
.
Sqrt
(
2
)
*
value
/
100.0
);
...
...
This diff is collapsed.
Click to expand it.
Source/Document Structure/SvgFragment.cs
+
15
-
37
View file @
13132300
...
...
@@ -15,28 +15,9 @@ namespace Svg
/// </summary>
public
static
readonly
Uri
Namespace
=
new
Uri
(
"http://www.w3.org/2000/svg"
);
Point
F
ISvgBoundable
.
Location
Rectangle
F
ISvgBoundable
.
CalculateBounds
()
{
get
{
return
PointF
.
Empty
;
}
}
SizeF
ISvgBoundable
.
Size
{
get
{
return
GetDimensions
();
}
}
RectangleF
ISvgBoundable
.
Bounds
{
get
{
return
new
RectangleF
(((
ISvgBoundable
)
this
).
Location
,
((
ISvgBoundable
)
this
).
Size
);
}
return
new
RectangleF
(
PointF
.
Empty
,
GetDimensions
());
}
private
SvgUnit
_x
;
...
...
@@ -185,32 +166,29 @@ namespace Svg
break
;
}
}
/// <summary>
/// Gets the <see cref="GraphicsPath"/> for this element.
/// </summary>
/// <value></value>
public
GraphicsPath
Path
public
GraphicsPath
Create
Path
()
{
get
{
var
path
=
new
GraphicsPath
();
var
path
=
new
GraphicsPath
();
AddPaths
(
this
,
path
);
return
path
;
}
AddPaths
(
this
,
path
);
return
path
;
}
/// <summary>
/// Gets the bounds of the svg element.
/// </summary>
/// <
value
>The bounds.</
value
>
public
RectangleF
Bounds
{
get
/// <
returns
>The bounds.</
returns
>
public
RectangleF
Calculate
Bounds
()
{
using
(
var
path
=
CreatePath
())
{
return
this
.
P
ath
.
GetBounds
();
return
p
ath
.
GetBounds
();
}
}
...
...
@@ -242,7 +220,7 @@ namespace Svg
}
else
{
bounds
=
this
.
Bounds
;
//do just one call to the
recur
sive bounds
property
bounds
=
this
.
Calculate
Bounds
()
;
//do just one call to the
expen
sive bounds
calculation method
}
}
...
...
This diff is collapsed.
Click to expand it.
Source/Document Structure/SvgGroup.cs
+
18
-
21
View file @
13132300
...
...
@@ -21,35 +21,32 @@ namespace Svg
/// <summary>
/// Gets the bounds of the element.
/// </summary>
/// <
value
>The bounds.</
value
>
public
override
System
.
Drawing
.
RectangleF
Bounds
/// <
returns
>The bounds.</
returns
>
public
override
RectangleF
Calculate
Bounds
()
{
get
{
var
r
=
new
RectangleF
();
foreach
(
var
c
in
this
.
Childr
en
)
var
r
=
new
RectangleF
();
foreach
(
var
c
in
this
.
Children
)
{
if
(
c
is
SvgVisualElem
en
t
)
{
if
(
c
is
SvgVisualElement
)
// First it should check if rectangle is empty or it will return the wrong Bounds.
// This is because when the Rectangle is Empty, the Union method adds as if the first values where X=0, Y=0
if
(
r
.
IsEmpty
)
{
// First it should check if rectangle is empty or it will return the wrong Bounds.
// This is because when the Rectangle is Empty, the Union method adds as if the first values where X=0, Y=0
if
(
r
.
IsEmpty
)
{
r
=
((
SvgVisualElement
)
c
).
Bounds
;
}
else
r
=
((
SvgVisualElement
)
c
).
CalculateBounds
();
}
else
{
var
childBounds
=
((
SvgVisualElement
)
c
).
CalculateBounds
();
if
(!
childBounds
.
IsEmpty
)
{
var
childBounds
=
((
SvgVisualElement
)
c
).
Bounds
;
if
(!
childBounds
.
IsEmpty
)
{
r
=
RectangleF
.
Union
(
r
,
childBounds
);
}
r
=
RectangleF
.
Union
(
r
,
childBounds
);
}
}
}
return
r
;
}
return
r
;
}
protected
override
bool
Renderable
{
get
{
return
false
;
}
}
...
...
This diff is collapsed.
Click to expand it.
Source/Document Structure/SvgSwitch.cs
+
17
-
20
View file @
13132300
...
...
@@ -25,35 +25,32 @@ namespace Svg
/// <summary>
/// Gets the bounds of the element.
/// </summary>
/// <
value
>The bounds.</
value
>
public
override
System
.
Drawing
.
RectangleF
Bounds
/// <
returns
>The bounds.</
returns
>
public
override
RectangleF
Calculate
Bounds
()
{
get
var
r
=
new
RectangleF
();
foreach
(
var
c
in
this
.
Children
)
{
var
r
=
new
RectangleF
();
foreach
(
var
c
in
this
.
Children
)
if
(
c
is
SvgVisualElement
)
{
if
(
c
is
SvgVisualElement
)
// First it should check if rectangle is empty or it will return the wrong Bounds.
// This is because when the Rectangle is Empty, the Union method adds as if the first values where X=0, Y=0
if
(
r
.
IsEmpty
)
{
// First it should check if rectangle is empty or it will return the wrong Bounds.
// This is because when the Rectangle is Empty, the Union method adds as if the first values where X=0, Y=0
if
(
r
.
IsEmpty
)
{
r
=
((
SvgVisualElement
)
c
).
Bounds
;
}
else
r
=
((
SvgVisualElement
)
c
).
CalculateBounds
();
}
else
{
var
childBounds
=
((
SvgVisualElement
)
c
).
CalculateBounds
();
if
(!
childBounds
.
IsEmpty
)
{
var
childBounds
=
((
SvgVisualElement
)
c
).
Bounds
;
if
(!
childBounds
.
IsEmpty
)
{
r
=
RectangleF
.
Union
(
r
,
childBounds
);
}
r
=
RectangleF
.
Union
(
r
,
childBounds
);
}
}
}
return
r
;
}
return
r
;
}
/// <summary>
...
...
This diff is collapsed.
Click to expand it.
Source/Document Structure/SvgSymbol.cs
+
16
-
19
View file @
13132300
...
...
@@ -48,34 +48,31 @@ namespace Svg.Document_Structure
/// Gets the bounds of the element.
/// </summary>
/// <value>The bounds.</value>
public
override
System
.
Drawing
.
RectangleF
Bounds
public
override
System
.
Drawing
.
RectangleF
Calculate
Bounds
()
{
get
var
r
=
new
RectangleF
();
foreach
(
var
c
in
this
.
Children
)
{
var
r
=
new
RectangleF
();
foreach
(
var
c
in
this
.
Children
)
if
(
c
is
SvgVisualElement
)
{
if
(
c
is
SvgVisualElement
)
// First it should check if rectangle is empty or it will return the wrong Bounds.
// This is because when the Rectangle is Empty, the Union method adds as if the first values where X=0, Y=0
if
(
r
.
IsEmpty
)
{
// First it should check if rectangle is empty or it will return the wrong Bounds.
// This is because when the Rectangle is Empty, the Union method adds as if the first values where X=0, Y=0
if
(
r
.
IsEmpty
)
{
r
=
((
SvgVisualElement
)
c
).
Bounds
;
}
else
r
=
((
SvgVisualElement
)
c
).
CalculateBounds
();
}
else
{
var
childBounds
=
((
SvgVisualElement
)
c
).
CalculateBounds
();
if
(!
childBounds
.
IsEmpty
)
{
var
childBounds
=
((
SvgVisualElement
)
c
).
Bounds
;
if
(!
childBounds
.
IsEmpty
)
{
r
=
RectangleF
.
Union
(
r
,
childBounds
);
}
r
=
RectangleF
.
Union
(
r
,
childBounds
);
}
}
}
return
r
;
}
return
r
;
}
protected
override
bool
Renderable
{
get
{
return
false
;
}
}
...
...
This diff is collapsed.
Click to expand it.
Source/Document Structure/SvgUse.cs
+
3
-
2
View file @
13132300
using
System
;
using
System.Collections.Generic
;
using
System.Drawing
;
using
System.Text
;
using
System.Web
;
using
System.Xml
;
...
...
@@ -61,9 +62,9 @@ namespace Svg
return
(
element
!=
null
)
?
element
.
Path
(
renderer
)
:
null
;
}
public
override
System
.
Drawing
.
RectangleF
Bounds
public
override
RectangleF
Calculate
Bounds
()
{
get
{
return
new
System
.
Drawing
.
RectangleF
();
}
return
new
System
.
Drawing
.
RectangleF
();
}
protected
override
bool
Renderable
{
get
{
return
false
;
}
}
...
...
This diff is collapsed.
Click to expand it.
Source/Extensibility/SvgForeignObject.cs
+
17
-
20
View file @
13132300
...
...
@@ -25,35 +25,32 @@ namespace Svg
/// <summary>
/// Gets the bounds of the element.
/// </summary>
/// <
value
>The bounds.</
value
>
public
override
System
.
Drawing
.
RectangleF
Bounds
/// <
returns
>The bounds.</
returns
>
public
override
RectangleF
Calculate
Bounds
()
{
get
var
r
=
new
RectangleF
();
foreach
(
var
c
in
this
.
Children
)
{
var
r
=
new
RectangleF
();
foreach
(
var
c
in
this
.
Children
)
if
(
c
is
SvgVisualElement
)
{
if
(
c
is
SvgVisualElement
)
// First it should check if rectangle is empty or it will return the wrong Bounds.
// This is because when the Rectangle is Empty, the Union method adds as if the first values where X=0, Y=0
if
(
r
.
IsEmpty
)
{
// First it should check if rectangle is empty or it will return the wrong Bounds.
// This is because when the Rectangle is Empty, the Union method adds as if the first values where X=0, Y=0
if
(
r
.
IsEmpty
)
{
r
=
((
SvgVisualElement
)
c
).
Bounds
;
}
else
r
=
((
SvgVisualElement
)
c
).
CalculateBounds
();
}
else
{
var
childBounds
=
((
SvgVisualElement
)
c
).
CalculateBounds
();
if
(!
childBounds
.
IsEmpty
)
{
var
childBounds
=
((
SvgVisualElement
)
c
).
Bounds
;
if
(!
childBounds
.
IsEmpty
)
{
r
=
RectangleF
.
Union
(
r
,
childBounds
);
}
r
=
RectangleF
.
Union
(
r
,
childBounds
);
}
}
}
return
r
;
}
return
r
;
}
protected
override
bool
Renderable
{
get
{
return
false
;
}
}
...
...
This diff is collapsed.
Click to expand it.
Source/Painting/GenericBoundable.cs
+
2
-
12
View file @
13132300
...
...
@@ -19,19 +19,9 @@ namespace Svg
_rect
=
new
RectangleF
(
x
,
y
,
width
,
height
);
}
public
System
.
Drawing
.
PointF
Location
public
RectangleF
CalculateBounds
()
{
get
{
return
_rect
.
Location
;
}
}
public
System
.
Drawing
.
SizeF
Size
{
get
{
return
_rect
.
Size
;
}
}
public
System
.
Drawing
.
RectangleF
Bounds
{
get
{
return
_rect
;
}
return
_rect
;
}
}
}
This diff is collapsed.
Click to expand it.
Source/Painting/ISvgBoundable.cs
+
1
-
14
View file @
13132300
...
...
@@ -4,19 +4,6 @@ namespace Svg
{
public
interface
ISvgBoundable
{
PointF
Location
{
get
;
}
SizeF
Size
{
get
;
}
RectangleF
Bounds
{
get
;
}
RectangleF
CalculateBounds
();
}
}
\ No newline at end of file
This diff is collapsed.
Click to expand it.
Source/Painting/ImmutableBoundable.cs
0 → 100644
+
19
-
0
View file @
13132300
using
System.Drawing
;
namespace
Svg
{
internal
sealed
class
ImmutableBoundable
:
ISvgBoundable
{
private
readonly
RectangleF
bounds
;
public
ImmutableBoundable
(
ISvgBoundable
boundable
)
{
bounds
=
boundable
.
CalculateBounds
();
}
public
RectangleF
CalculateBounds
()
{
return
bounds
;
}
}
}
\ No newline at end of file
This diff is collapsed.
Click to expand it.
Source/Painting/SvgGradientServer.cs
+
1
-
1
View file @
13132300
...
...
@@ -178,7 +178,7 @@ namespace Svg
for
(
int
i
=
0
;
i
<
colourBlends
;
i
++)
{
var
currentStop
=
this
.
Stops
[
radial
?
this
.
Stops
.
Count
-
1
-
actualStops
:
actualStops
];
var
boundWidth
=
renderer
.
GetBoundable
().
Bounds
.
Width
;
var
boundWidth
=
renderer
.
GetBoundable
().
Calculate
Bounds
()
.
Width
;
mergedOpacity
=
opacity
*
currentStop
.
GetOpacity
();
position
=
...
...
This diff is collapsed.
Click to expand it.
Source/Painting/SvgLinearGradientServer.cs
+
2
-
2
View file @
13132300
...
...
@@ -191,7 +191,7 @@ namespace Svg
private
LinePoints
PointsToMove
(
ISvgBoundable
boundable
,
PointF
specifiedStart
,
PointF
specifiedEnd
)
{
var
bounds
=
boundable
.
Bounds
;
var
bounds
=
boundable
.
Calculate
Bounds
()
;
if
(
specifiedStart
.
X
==
specifiedEnd
.
X
)
{
return
(
bounds
.
Top
<
specifiedStart
.
Y
&&
specifiedStart
.
Y
<
bounds
.
Bottom
?
LinePoints
.
Start
:
LinePoints
.
None
)
|
...
...
@@ -227,7 +227,7 @@ namespace Svg
return
new
GradientPoints
(
specifiedStart
,
specifiedEnd
);
}
var
bounds
=
boundable
.
Bounds
;
var
bounds
=
boundable
.
Calculate
Bounds
()
;
var
effectiveStart
=
specifiedStart
;
var
effectiveEnd
=
specifiedEnd
;
var
intersectionPoints
=
CandidateIntersections
(
bounds
,
specifiedStart
,
specifiedEnd
);
...
...
This diff is collapsed.
Click to expand it.
Source/Painting/SvgMarker.cs
+
5
-
8
View file @
13132300
...
...
@@ -95,15 +95,12 @@ namespace Svg
return
null
;
}
public
override
System
.
Drawing
.
RectangleF
Bounds
public
override
RectangleF
Calculate
Bounds
()
{
get
{
var
path
=
this
.
Path
(
null
);
if
(
path
!=
null
)
return
path
.
GetBounds
();
return
new
System
.
Drawing
.
RectangleF
();
}
var
path
=
this
.
Path
(
null
);
if
(
path
!=
null
)
return
path
.
GetBounds
();
return
new
System
.
Drawing
.
RectangleF
();
}
public
override
SvgElement
DeepCopy
()
...
...
This diff is collapsed.
Click to expand it.
Prev
1
2
Next
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment
Menu
Projects
Groups
Snippets
Help