PMA.UI Examples 2.43.3by Pathomation

Magic wand and brush

expertslide loaderannotationsmagic wandbrush
Console
PMA.UI version: 2.43.3
Annotations example, showing the usage of magic wand and brush to create annotations
annotations_brush_wand.html
1<!doctype html>
2<html lang="en">
3
4<head>
5    <meta charset="utf-8">
6    <meta http-equiv="X-UA-Compatible" content="IE=10">
7    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
8
9    <!-- Include PMA.UI required libraries downloaded or from CDN -->
10    <script src="./pma.ui/jquery-3.1.0.js"></script>
11    <link href="./pma.ui/font-awesome.min.css" type="text/css" rel="stylesheet">
12
13    <!-- Include optional libraries downloaded or from CDN -->
14    <link rel="stylesheet" href="./pma.ui/bootstrap.min.css">
15    <script src="./pma.ui/bootstrap.bundle.min.js"></script>
16
17    <!-- Include PMA.UI script & css -->
18    <script src="./pma.ui/pma.ui.js"></script>
19    <link href="./pma.ui/pma.ui.css" type="text/css" rel="stylesheet">
20
21    <!-- Include custom script & css -->
22    <script src="./js/annotations_brush_wand.js"></script>
23    <link href="./css/annotations_brush_wand.css" type="text/css" rel="stylesheet">
24
25    <title>Annotations - Magic wand and brush</title>
26</head>
27
28<body>
29    <div class="container-xl">
30        <div class="row">
31            <div class="col-auto annotations">
32                <div class="btn-group-vertical btn-group-md">
33                    <button type="button" class="btn btn-light" disabled data-action="edit" data-type="Wand"
34                        title="Magic Wand">
35                        <i class="fas fa-magic"></i>
36                    </button>
37                    <button type="button" class="btn btn-light" disabled data-action="edit" data-type="Brush"
38                        title="Brush">
39                        <i class="fas fa-paint-brush"></i>
40                    </button>
41                    <input id="color-picker-native" type='color' value='#78eb10' class="color-picker" />
42                    <input id="color-picker-js" type='button' value=' ' class="btn btn-light" style="width: 50px" />
43                    <button type="button" class="btn btn-light color-btn" data-color="#0000ff" style="color:#0000ff">
44                        <i class='fa fa-square'></i>
45                    </button>
46                    <button type="button" class="btn btn-light color-btn" data-color="#ff0000" style="color:#ff0000">
47                        <i class='fa fa-square'></i>
48                    </button>
49                    <button type="button" class="btn btn-light color-btn" data-color="#00ff00" style="color:#00ff00">
50                        <i class='fa fa-square'></i>
51                    </button>
52                </div>
53                <input id="color-picker-hidden" type='hidden' value='#78eb10' class="color-picker" />
54            </div>
55            <div class="col">
56                <div class="row top-row">
57                    <div class="col">
58                        <div class="btn-group btn-group-sm">
59                            <button type="button" class="btn btn-secondary" disabled data-action="delete" data-type
60                                title="Delete">
61                                <i class="fas fa-times"></i>
62                            </button>
63                            <button type="button" class="btn btn-secondary" disabled data-action="deleteall" data-type
64                                title="Delete all">
65                                <i class="fas fa-times-circle"></i>
66                            </button>
67                            <button type="button" class="btn btn-secondary" disabled data-action="save" data-type
68                                title="Save">
69                                <i class="fas fa-save"></i>
70                            </button>
71                            <button type="button" class="btn btn-secondary" disabled id="version" title="Version">
72                                <i class="fas fa-info"></i>
73                            </button>
74                        </div>
75
76                        <div class="btn-group btn-group-sm btn-group-toggle" data-toggle="buttons">
77                            <button class="btn btn-secondary active" disabled title="Toggle annotation labels">
78                                <input id="chkLabels" type="checkbox" autocomplete="off" />
79                                <i class="fas fa-font"></i>
80                            </button>
81                            <button class="btn btn-secondary active" disabled title="Toggle annotation measurements">
82                                <input id="chkMeasurements" type="checkbox" autocomplete="off" />
83                                <i class="fas fa-tag"></i>
84                            </button>
85                            <button class="btn btn-secondary" disabled title="Toggle annotations loaded by fingerprint">
86                                <input id="chkFingerprint" type="checkbox" autocomplete="off" />
87                                <i class="fas fa-id-card"></i>
88                            </button>
89                        </div>
90
91                        <span class="form-group" style="margin-right: 15px;">
92                            <input type="text" class="form-control  form-control-sm" disabled id="annotation-text"
93                                style="display:initial; width:120px" />
94                            <span class="badge badge-light">
95                                Length
96                                <span id="txt-length"> - </span>
97                            </span>
98                            <span class="badge badge-light" style="margin-left: 10px">
99                                Area
100                                <span id="txt-area"> - </span>
101                            </span>
102                        </span>
103
104                        <span class="annotation-helper-icon">
105                            <i class="fas fa-spinner fa-spin"></i>
106                            <span class="badge badge-success"><i class="fas fa-check"></i> Saved</span>
107                            <span class="badge badge-error"><i class="fas fa-times"></i> Error</span>
108                        </span>
109                    </div>
110                </div>
111                <div class="row viewer-row">
112                    <div class="col">
113                        <!-- The element that will host the slide loader -->
114                        <div id="viewer"></div>
115                    </div>
116                </div>
117            </div>
118</body>
119
120</html>
annotations_brush_wand.css
1html,
2body
3{
4    height: 100%;
5    padding: 0px;
6    margin: 0px;
7}
8
9body .pma-ui-viewport-container {
10    height: 100%;
11    width: 100%;
12}
13
14.aligned-text {
15    display: inline-block;
16    text-align: right;
17    width: 70px;
18}
19
20.annotation-helper-icon i,
21.annotation-helper-icon span {
22    display: none;
23}
24
25.annotation-helper-icon.loading i {
26    display: initial;
27}
28
29.annotation-helper-icon.loading span {
30    display: none;
31}
32
33.annotation-helper-icon.saved i,
34.annotation-helper-icon.saved span.badge-danger {
35    display: none;
36}
37
38.annotation-helper-icon.saved span.badge-success {
39    display: initial;
40}
41
42.annotation-helper-icon.error i,
43.annotation-helper-icon.error span.badge-success {
44    display: none;
45}
46
47.annotation-helper-icon.error span.badge-danger {
48    display: block;
49}
50
51.annotations {
52    margin: 0 auto;
53    padding: 0;
54}
55.viewer-row {
56    height: calc(100vh - 33px);
57}
58.color-picker{
59    height: 35px;
60    margin-bottom: 1px;
61    border: none;
62}
annotations_brush_wand.js
1// Initial declarations
2var serverUrl = "https://host.pathomation.com/pma.core.2/";
3var serverUsername = "PMA_UI_demo";
4var serverPassword = "PMA_UI_demo";
5var caller = "DemoPortal";
6var slideLoaderElementSelector = "#viewer";
7var imagePath = "Reference/3DHistech/CMU-1.mrxs";
8var context = null;
9var slideLoader = null;
10var annotationManager = null;
11var brushSize = 1000;
12
13function supportsColorPicker() {
14    var colorInput;
15    colorInput = $('<input type="color" value="!" />')[0];
16    return colorInput.type === 'color' && colorInput.value !== '!';
17}
18
19function drawCommands(action, type) {
20    if (action) {
21        if (action === "draw") {
22            var color = $(".color-picker").val();
23
24            var f = annotationManager.getSelection();
25
26            annotationManager.startDrawing({
27                type: type,
28                color: color,
29                fillColor: "rgba(33,44,55,255)",
30                lineThickness: Math.floor(Math.random() * 15) + 1,
31                iconRelativePath: null,
32                feature: type === "MultiPoint" && f.length > 0 ? f[0] : undefined,
33                notes: $("#annotation-text").val() ? $("#annotation-text").val() : " "
34            });
35        } else if (action === "edit") {
36            var f = annotationManager.getSelection();
37
38            annotationManager.enterEditMode({
39                type: type,
40                brushType: 'circle',
41                brushSize: brushSize,
42                iconRelativePath: null,
43                drawMode: true,
44                feature: annotationManager.getSelection()[0],
45                color: $(".color-picker").val(),
46                fillColor: "rgba(33,44,55, " + Math.random() + ")",
47                lineThickness: Math.floor(Math.random() * 15) + 1,
48                notes: $("#annotation-text").val() ? $("#annotation-text").val() : " "
49            });
50        } else if (action === "measure") {
51            if (type === "clear") {
52                slideLoader.mainViewport.stopMeasuring();
53                return;
54            }
55
56            slideLoader.mainViewport.startMeasuring(type);
57        } else if (action === "save") {
58            saveAnnotations();
59        } else if (action === "delete") {
60            var ann = annotationManager.getSelection();
61
62            if (ann && ann.length > 0) {
63                annotationManager.deleteAnnotation(ann[0].getId());
64            }
65        } else if (action === "deleteall") {
66            var allAnnotations = slideLoader.mainViewport.getAnnotations();
67
68            for (var i = 0; i < allAnnotations.length; i++) {
69                annotationManager.deleteAnnotation(allAnnotations[i].getId());
70            }
71        } else if (action === "merge") {
72            annotationManager.mergeSelection();
73        } else if (action === "union") {
74            annotationManager.booleanUnion(annotationManager.getSelection());
75        } else if (action === "difference") {
76            annotationManager.booleanDifference(annotationManager.getSelection()[0], annotationManager.getSelection()[1]);
77        }
78    }
79}
80
81function saveAnnotations(e) {
82    if (e) {
83        var metadata = e.hasOwnProperty("feature") ? e.feature.metaData : (e.hasOwnProperty("length") && e.length !== 0 ? e[0].metaData : null);
84        if (metadata) {
85            metadata.Notes = $("#annotation-text").val() ? $("#annotation-text").val() : " ";
86        }
87    }
88
89    $(".annotation-helper-icon").addClass("loading");
90    annotationManager.saveAnnotations();
91}
92
93jQuery(function () {
94    var jsColorPicker = null;
95    if (supportsColorPicker()) {
96        $("#color-picker-js").remove();
97        $("#color-picker-hidden").remove();
98    } else {
99        $("#color-picker-native").remove();
100        jsColorPicker = new jscolor($("#color-picker-js")[0], {
101            valueElement: $("#color-picker-hidden")[0],
102            hash: true,
103            closable: true,
104            closeText: "Close"
105        });
106    }
107
108    $("button.color-btn").on("click", function (e) {
109        var clr = $(this).data("color");
110        $("#color-picker-native").val(clr);
111        if (jsColorPicker) {
112            jsColorPicker.fromString(clr);
113        }
114    });
115
116    $("button[data-action][data-type], a[data-action][data-type]").on("click", function (e) {
117        e.preventDefault();
118        drawCommands($(this).data("action"), $(this).data("type"));
119    });
120
121    $("#version").on("click", function (e) {
122        PMA.UI.Components.callApiMethod({
123            serverUrl: serverUrl,
124            method: PMA.UI.Components.ApiMethods.GetVersionInfo,
125            httpMethod: "GET",
126            data: {
127                username: serverUsername,
128                password: serverPassword,
129                caller: caller,
130            },
131            success: function (http) {
132                console.log("PMA.core version: " + JSON.parse(http.responseText));
133            },
134            failure: function (http) {
135                console.log(http);
136            },
137        });
138    });
139
140    console.log(`PMA.UI version: ${PMA.UI.getVersion()}`);
141
142    // Create a context
143    context = new PMA.UI.Components.Context({ caller: caller });
144
145    // Add an autologin authentication provider
146    new PMA.UI.Authentication.AutoLogin(context, [{ serverUrl: serverUrl, username: serverUsername, password: serverPassword }]);
147
148    // Create an image loader that will allow us to load images easily
149    slideLoader = new PMA.UI.Components.SlideLoader(context, {
150        element: slideLoaderElementSelector,
151        annotations: {},
152    });
153
154    // Listen for the slide loaded event by the slide loader
155    slideLoader.listen(PMA.UI.Components.Events.SlideLoaded, function (args) {
156        $("button, input").removeAttr("disabled");
157        annotationManager = new PMA.UI.Components.Annotations({
158            context: context,
159            element: null,
160            viewport: slideLoader.mainViewport,
161            serverUrl: args.serverUrl,
162            path: args.path,
163            enabled: true
164        });
165
166        annotationManager.listen(PMA.UI.Components.Events.AnnotationAdded, function (e) {
167            console.log("Annotation added", e.feature);
168        });
169
170        annotationManager.listen(PMA.UI.Components.Events.AnnotationsSelectionChanged, function (e) {
171            if (e) {
172                $("button[data-action='delete']").attr("disabled", false);
173                var metadata = e.hasOwnProperty("feature") ? e.feature.metaData : (e.hasOwnProperty("length") && e.length !== 0 ? e[0].metaData : null);
174                if (metadata) {
175                    console.log("selection: ", metadata);
176                    $("#annotation-text").val(metadata.Notes);
177                    $("#txt-area").html(metadata.FormattedArea || " - ");
178                    $("#txt-length").html(metadata.FormattedLength || " - ");
179                } else {
180                    $("button[data-action='delete']").attr("disabled", true);
181                    $("#annotation-text").val("");
182                    $("#txt-area").html(" - ");
183                    $("#txt-length").html(" - ");
184                }
185            }
186        });
187
188        annotationManager.listen(PMA.UI.Components.Events.AnnotationsSaved, function (e) {
189            if (e && e.success) {
190                $(".annotation-helper-icon").removeClass("loading").addClass("saved");
191            } else {
192                $(".annotation-helper-icon").removeClass("loading").addClass("error");
193            }
194
195            setTimeout(function () {
196                $(".annotation-helper-icon").removeClass("loading error saved");
197            }, 1000);
198
199            var labels = $("#chkLabels").parent().hasClass("active");
200            var measurements = $("#chkMeasurements").parent().hasClass("active");
201            slideLoader.mainViewport.showAnnotationsLabels(labels, measurements);
202        });
203
204        annotationManager.listen(PMA.UI.Components.Events.AnnotationDrawing, function (e) {
205            // Event called while drawing annotations
206        });
207
208        annotationManager.listen(PMA.UI.Components.Events.AnnotationEditingEnded, function (e) {
209            if (e && e.edit && e.edit.getBrushSize) {
210                brushSize = e.edit.getBrushSize() ?? 1000;
211            }
212        });
213
214        annotationManager.listen(PMA.UI.Components.Events.AnnotationDeleted, function (e) {
215            console.log("Annotation deleted", e.feature);
216        });
217    });
218
219    // Load the image with the context
220    slideLoader.load(serverUrl, imagePath);
221
222    $("#annotation-text").on("focusout", function () {
223        if (annotationManager && annotationManager.getSelection().length !== 0) {
224            var sel = annotationManager.getSelection()[0];
225            if (sel.metaData) {
226                sel.metaData.Notes = $(this).val() ? $(this).val() : " ";
227                annotationManager.saveAnnotations();
228                annotationManager.clearSelection();
229                var labels = $("#chkLabels").parent().hasClass("active");
230                var measurements = $("#chkMeasurements").parent().hasClass("active");
231                slideLoader.mainViewport.showAnnotationsLabels(labels, measurements);
232            }
233        }
234    });
235
236    $("body").on("keydown", function (event) {
237        if (!$(event.target).is('input') && (event.key === "Delete" || event.key === "Del")) {
238            drawCommands("delete", null);
239        }
240    });
241
242    $("#chkLabels").parent().on("click", function () {
243        var labels = !$("#chkLabels").parent().hasClass("active");
244        var measurements = $("#chkMeasurements").parent().hasClass("active");
245        console.log(`Show labels: ${labels} - Show measurements: ${measurements}`);
246        slideLoader.mainViewport.showAnnotationsLabels(labels, measurements);
247    });
248
249    $("#chkMeasurements").parent().on("click", function () {
250        var labels = $("#chkLabels").parent().hasClass("active");
251        var measurements = !$("#chkMeasurements").parent().hasClass("active");
252        console.log(`Show labels: ${labels} - Show measurements: ${measurements}`);
253        slideLoader.mainViewport.showAnnotationsLabels(labels, measurements);
254    });
255
256    $("#chkFingerprint").parent().on("click", function () {
257        var fingerprint = !$("#chkFingerprint").parent().hasClass("active");
258        slideLoader.mainViewport.showAnnotationsByFingerprint(fingerprint);
259    });
260});