I want to change the image of marker-arrowheads to achieve uniformity for my web application.
Link Image
Is there a simple and possible way to change the image of marker-arrowheads.
I solved the problem by following the below mentioned solution:
link.attr({
'.marker-arrowhead[end="source"]': { fill: 'red', d: 'M 10 0 L 0 5 L 10 10 z' },
'.marker-arrowhead[end="target"]': { fill: 'yellow', d: 'M 10 0 L 0 5 L 10 10 z' }
});
Related
After decades of writing MFC apps, I'm trying to learn C++/WinRT and WinUI 3. To do this, I'm working through Petzold's "Programming Windows" 6th edition, converting the code from C# to C++/WinRT. I'm in Chapter 2, working on the PathMarkupSyntaxCode example. Here's the C# code:
public MainPage()
{
this.InitializeComponent();
Path path = new Path
{
Stroke = new SolidColorBrush(Colors.Red),
StrokeThickness = 12,
StrokeLineJoin = PenLineJoin.Round,
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
Data = PathMarkupToGeometry(
"M 0 0 L 0 100 M 0 50 L 50 50 M 50 0 L 50 100 " +
"M 125 0 C 60 -10, 60 60, 125 50, 60 40, 60 110, 125 100 " +
"M 150 0 L 150 100, 200 100 " +
"M 225 0 L 225 100, 275 100 " +
"M 300 50 A 25 50 0 1 0 300 49.9")
};
(this.Content as Grid).Children.Add(path);
}
Geometry PathMarkupToGeometry(string pathMarkup)
{
string xaml =
"<Path " +
"xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'>" +
"<Path.Data>" + pathMarkup + "</Path.Data></Path>";
Path path = XamlReader.Load(xaml) as Path;
// Detach the PathGeometry from the Path
Geometry geometry = path.Data;
path.Data = null;
return geometry;
}
and here's my (non-functional) C++/WinRT code:
MainWindow::MainWindow()
{
InitializeComponent();
std::string str;
str = "M 0 0 L 0 100 M 0 50 L 50 50 M 50 0 L 50 100 ";
str += "M 125 0 C 60 -10, 60 60, 125 50, 60 40, 60 110, 125 100 ";
str += "M 150 0 L 150 100, 200 100 ";
str += "M 225 0 L 225 100, 275 100 ";
str += "M 300 50 A 25 50 0 1 0 300 49.9";
Path path;
path.Stroke(SolidColorBrush(Colors::Red()));
path.StrokeThickness(12);
path.StrokeLineJoin(PenLineJoin::Round);
path.HorizontalAlignment(HorizontalAlignment::Center);
path.VerticalAlignment(VerticalAlignment::Center);
path.Data(PathMarkupToGeometry(str));
Grid().Children().Append(path);
}
Geometry MainWindow::PathMarkupToGeometry(const std::string& pathMarkup)
{
std::string xaml = "<Path ";
xaml += "xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'>";
xaml += "<Path.Data>";
xaml += pathMarkup;
xaml += "</Path.Data></Path>";
hstring str = winrt::to_hstring(xaml);
auto tmpl = XamlReader::Load(str);
Path path(tmpl.try_as<Path>());
Geometry geometry = path.Data();
return geometry;
}
The code in the first function that sets path.Data results in an error being thrown in Microsoft.UI.Xaml.h, in the function OnLaunched in the struct produce(). The reason for the error is that the parameter 'args' is 0.
It took me several hours to get this to even compile. (Thanks to IInspectable for the reference to try_as in a comment to another question.) I'm hoping someone who knows C++/WinRT can easily see my error. Any help would be greatly appreciated.
I was hoping the above code would print HELLO on the main window. Everything seems to be going fine in the second function until tmpl gets turned into a Path. After that, I don't know how to tell if the data is being passed correctly or not.
You forgot to detach the geometry from the path, and the constructor just creates a new grid, it doesn't gets the one from current content.
The strict equivalent would be this (note it's better to use std::wstring to benefit from natural hstring conversions - Windows is all Unicode for a long time now):
using namespace winrt;
using namespace Windows::Foundation;
using namespace Microsoft::UI;
using namespace Microsoft::UI::Xaml;
using namespace Microsoft::UI::Xaml::Controls;
using namespace Microsoft::UI::Xaml::Media;
using namespace Microsoft::UI::Xaml::Markup;
using namespace Microsoft::UI::Xaml::Shapes;
...
MainWindow::MainWindow()
{
InitializeComponent();
std::wstring str;
str = L"M 0 0 L 0 100 M 0 50 L 50 50 M 50 0 L 50 100 ";
str += L"M 125 0 C 60 -10, 60 60, 125 50, 60 40, 60 110, 125 100 ";
str += L"M 150 0 L 150 100, 200 100 ";
str += L"M 225 0 L 225 100, 275 100 ";
str += L"M 300 50 A 25 50 0 1 0 300 49.9";
Path path;
path.Stroke(SolidColorBrush(Colors::Red()));
path.StrokeThickness(12);
path.StrokeLineJoin(PenLineJoin::Round);
path.HorizontalAlignment(HorizontalAlignment::Center);
path.VerticalAlignment(VerticalAlignment::Center);
path.Data(PathMarkupToGeometry(str));
Content().try_as<Grid>().Children().Append(path);
}
Geometry MainWindow::PathMarkupToGeometry(const std::wstring& pathMarkup)
{
std::wstring xaml = L"<Path ";
xaml += L"xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'>";
xaml += L"<Path.Data>";
xaml += pathMarkup;
xaml += L"</Path.Data></Path>";
auto path = XamlReader::Load(xaml).try_as<Path>();
Geometry geometry = path.Data();
path.Data(nullptr);
return geometry;
}
It's generally better to use XAML, bindings, templates, controls, custom controls, etc. than to load and mangle raw XAML "manually", but I've kept your original code spirit. Also Petzold's book might lag a bit with respect to the all new WinUI3. And all this is so much easier in C# but that's another subject :-)
I receive arbitrary path data that is sometimes in the 10s of thousands positions.
How would i normalize it so that the top and left positions are at 0 x 0?
So for a Path that was created at 1000x1000 would be:
M 1000 1000 L 1000 1050 L 1050 1100 Z
and after normalization would be:
M 0 0 L 0 50 L 50 100 Z
Example code:
path.pathData // M 1000x1000 L 1000x1050 L 1050x1100 Z
path.normalizeToPosition(0,0);
path.pathData // M 0x0 L 0x50 L 50x100 Z
I've seen this answer and it looks close to what I want but the difference is I want the top and left most positions to be 0x0.
To put it another way I want the viewBox to always start with "0 0" and the vector path top and left to be completely inside the viewBox area no matter what the current path data is.
Another example. If the path data is:
M -1000 -1000 L -1000 -1050 L -1050 -1100 Z
The function would modify it to
M 0 0 L 0 50 L 50 100 Z
You can use the SVG DOM path API. You'll need a polyfill on Chrome though.
var path = document.getElementById("path");
var segments = path.pathSegList;
var len = segments.numberOfItems;
var newPath = ""
for (var i=0; i<len; ++i) {
var pathSeg = segments[i];
switch(pathSeg.pathSegTypeAsLetter){
case 'M':
case 'L':
newPath += `${pathSeg.pathSegTypeAsLetter} ${pathSeg.x - segments[0].x} ${pathSeg.y - segments[0].y} `
break;
case 'Z':
newPath += 'Z';
break;
}
}
console.log(newPath);
path.setAttribute("d", newPath);
<svg>
<path id="path" d="M 1000 1000 L 1000 1050 L 1050 1100 Z"/>
</svg>
After profiling the code it looks like bbox function is being called repeatedly. I cannot remove marker-source, marker-target and connection-wrap because I need those features. Is there a way to improve performance?
Try to replace marker-source and marker-target with SVG Markers. It's relatively easy to implement if your application does not require different sizes and colors of markers. For instance:
Define you own marker arrow.
var arrowMarker = V('marker', {
viewBox: "0 0 10 10",
refX: 9,
refY: 5,
markerWidth: 6,
markerHeight: 6,
orient: "auto"
}, [
V('path', {
'd': "M 0 0 L 10 5 L 0 10 z",
'fill': 'green'
})
]);
Add the marker to paper's SVG Defs so it can be reused later for multiple times.
V(paper.defs).append(arrowMarker);
Finally put the marker on a link. Use the marker-end or marker-start property to define the target resp. source marker.
var link = new joint.dia.Link({
markup: '<path class="connection"/><path class="connection-wrap"/>',
attrs: {
'.connection': {
'stroke': 'green',
'stroke-width': 2,
'marker-end': 'url(#' + arrowMarker.attr('id') + ')'
}
}
});
There is a JSFiddle with an example and other useful performance tips.
How can I make the link to stay connected to a cell or a port in 1 point?
If the port is a circle, when the cell is moved around, the link end connected to it moves across that circle.
I want to be able to make it stay at the same point - the center of the circle's edge.
Desired:
What is happening when I move the cell down:
Thank you.
If someone is interested, I solved this with the following:
Using 2 shapes.
port shape
the magnet, this is a circle with radius 1 (making it a point)
portMarkup:
<g class="port port<%= id %>">
<path class="port-body port-magnet"/>
<path class="port-body port-shape"/>
<text class="port-label"/>
</g>
and attributes:
'.outPorts .port-shape': {
d: circleFromPath(5)
},
'.outPorts .port-magnet': {
magnet: true
},
'.port-magnet': {
visibility: 'hidden',
d: circleFromPath(1),
transform: 'translate(4, 1)'
}
The circleFromPath() id a custom function that takes a circle parameter and returns SVG of a circle with supplied radius produces by path (CoffeeScript:
circleFromPath = (r = 5) ->
d = r * 2
"""
M 0, 0
m #{-r}, 0
a #{r},#{r} 0 1,0 #{d},0
a #{r},#{r} 0 1,0 #{-d},0
"""
A Google Map marker can take a complex svg path as its icon as in something like:
var baseSvg = {
x1 : "m 0,0 l45,0 l 190,225 l -45,0 l -190,-225 z",
x2 : "m 225,0 l -45,0 l -190,225 l 45,0 l 190,-225 z"
};
var baseIcon = {
path: "M0,0 " + baseSvg["x1"] + baseSvg["x2"],
fillColor: "#000000",
fillOpacity: 1,
scale: .2,
strokeColor: "black",
strokeWeight: 0,
rotation: 15
};
which is then fed into a marker:
var marker = new google.maps.Marker({
position: new google.maps.LatLng(somelat, somelng),
icon: baseIcon
});
All good. But it only draws in a single colour (black in this example). So, can they only have a single colour or is there a way to have multi-coloured symbols? Using the example code, x1 would be red and x2 would be green.
Note that this construct is borrowed from here: How do I indicate transparency in an SVG path in Google Maps API v3? and it works nicely.
I just did the same thing and I think you have to draw two markers with essentially identical data, but with the path and in this case fillColor properties changed:
var baseSvg = {
x1 : "m 0,0 l45,0 l 190,225 l -45,0 l -190,-225 z",
x2 : "m 225,0 l -45,0 l -190,225 l 45,0 l 190,-225 z"
},
baseIcon = {
fillOpacity: 1,
scale: .2,
strokeColor: "black",
strokeWeight: 0,
rotation: 15
},
markerPos = new google.maps.LatLng(somelat, somelng),
// psuedo code for cloning an object since that
// is out of scope for this question
greenIcon = Object.clone(baseIcon),
redIcon = Object.clone(baseIcon);
greenIcon.path = baseSvg.x1;
greenIcon.fillColor = "#0f0";
redIcon.path = baseSvg.x2;
redIcon.fillColor = "#f00";
var marker1 = new google.maps.Marker({
position: markerPos,
map: map,
icon: greenIcon
});
var marker2 = new google.maps.Marker({
position: markerPos,
map: map,
icon: redIcon
});
obviously not optimized js, but you get the idea.
When it comes to opacity, strokeOpacity is your guy. Check the maps.Symbol class for more information on symbol properties.