[$this, 'render_block'] ]); } public function enqueue_admin_assets($hook) { $screen = get_current_screen(); if ($screen->base === 'media' && isset($_GET['item']) || !(($hook !== 'upload.php' && strpos($hook, 'post.php') === false))) { wp_enqueue_script('active-contour-class', plugin_dir_url(__FILE__) . 'assets/class.Contour.js', [], $this->version, true); wp_enqueue_script('active-contour-primitives-class', plugin_dir_url(__FILE__) . 'assets/class.Primitives.js', [], $this->version, true); wp_enqueue_script('active-contour-collection-class', plugin_dir_url(__FILE__) . 'assets/class.ContourCollection.js', [], $this->version, true); wp_enqueue_script('active-contour-calc-class', plugin_dir_url(__FILE__) . 'assets/class.ContourCalc.js', [], $this->version, true); wp_enqueue_script('active-contour-editor-class', plugin_dir_url(__FILE__) . 'assets/class.ContourEditor.js', ['jquery', 'wp-i18n' ], $this->version, true); wp_enqueue_script('active-contour-editor', plugin_dir_url(__FILE__) . 'assets/editor.js', ['jquery'], $this->version, true); wp_enqueue_style('active-contour-style', plugin_dir_url(__FILE__) . 'assets/editor.css', [], $this->version); } } public function enqueue_front_assets() { wp_enqueue_script('active-contour-front', plugin_dir_url(__FILE__) . 'assets/front.js', [], $this->version, true); wp_enqueue_style('active-contour-front-style', plugin_dir_url(__FILE__) . 'assets/front.css', [], $this->version); } public function add_metabox() { add_meta_box( 'active-contour-meta', esc_html(__('Active Contour', 'active-contour')), [$this, 'metabox_html'], 'attachment', 'normal', 'high' ); } public function attachment_fields_to_edit($form_fields, $post) { $data = get_post_meta($post->ID, '_active_contours', true); $form_fields['active_contour_json'] = [ 'label' => 'Контуры', 'input' => 'textarea', 'value' => $data, 'extra_rows' => [ 'nonce' => wp_nonce_field( 'active_contour_update_' . $post->ID, // Action. 'active_contour_update', // Nonce name. true, // Output referer? false // Echo? ), ], ]; return $form_fields; } public function save_attachment_data($data) { if (isset($_POST['id'], $_POST['changes'], $_POST['changes']['active_contour_json'], $_POST['changes']['active_contour_update_nonce'])) { $nonce = wp_unslash($_POST['changes']['active_contour_update_nonce']); $id = intval($_POST['id']); if (wp_verify_nonce($nonce, 'active_contour_update_' . $id)) { $newValue = wp_unslash($_POST['changes']['active_contour_json']); if (json_decode($newValue) !== null) { update_post_meta($id, '_active_contours', $newValue); } } } } public function metabox_html($post) { // textarea has been inserted as a fields_to_edit echo '
'; echo 'If you use GPT (or another AI with vision capabilities), you can try generating a contour of points using a prompt like the following:'; echo '

Based on this image, return a JSON object with the following structure:
{
"title": "Object name",
"tags": ["tag1", "tag2", "..."],
"dots": [{ "x": ..., "y": ... }, ...]
}
The dots array should contain normalized coordinates (values from 0 to 1) outlining the shape of the specified object.
Dots must form a closed shape. Do not round coordinates, leave precison as is.
Also include a meaningful title and relevant tags describing the object.
Repeat this format for each of the following objects: ENTER YOUR OBJECTS LIST.

'; echo 'You can then input the resulting objects one by one using the form below — they will be added to the contour list for further editing.
'; echo ''; echo '' . esc_html(__('Export', 'active-contour')) . '
'; } public function shortcode($atts) { $atts = shortcode_atts([ 'id' => 0, 'size' => 'full', 'cid' => '', 'group' => '', // * - any group 'tags' => '', 'align' => 'left' ], $atts); $id = intval($atts['id']); $meta = wp_get_attachment_metadata($id); if (empty($meta['sizes'][$atts['size']])) { $atts['size'] = 'full'; } if (!in_array($atts['align'], ['left', 'right', 'center', 'full'])) { $atts['align'] = 'left'; } $img_url = wp_get_attachment_image_url($id, $atts['size']); if (!$img_url) return '-- айди не верен --'; $data = get_post_meta($id, '_active_contours', true); if (!$data) { $data = '[]'; } if (!empty($atts['cid']) && preg_match_all('/(\d+)/', $atts['cid'], $m)) { $id_filter = $m[1]; } else { $id_filter = []; } if (!empty($atts['tags'])) { $tags = preg_split('/\s*,\s*/', $atts['tags']); } else { $tags = []; } $group = trim($atts['group']); $json = json_decode($data); $contours = []; $isEmptyConditions = empty($id_filter) && empty($tags) && empty($group); if ($json !== null && !$isEmptyConditions) { // group pre-validation $all_groups = []; if ($json !== null && !$isEmptyConditions) { foreach($json as $contour) { if (property_exists($contour, 'group')) { $all_groups[$contour->group] = 1; } } } if (!isset($all_groups[$group]) && $group !== '*') { $group = ''; } foreach($json as $contour) { if (!empty($id_filter) && !in_array($contour->id, $id_filter)) { continue; } if (!empty($tags) && count(array_intersect($contour->tags, $tags)) == 0) { continue; } if (!empty($group)) { if ($group !== '*' && (!property_exists($contour, 'group') || $contour->group != $group)) continue; } $point_string = implode(' ', array_map(function($p) { return $p->x . ',' . $p->y; }, $contour->dots)); $classes = [ 'animation-' . (empty($contour->animation) ? 'default' : esc_attr($contour->animation)), 'effect-' . (empty($contour->hoverEffect) ? 'default' : esc_attr($contour->hoverEffect)), ]; $polygon = 'url) . '" target="_blank"') . '>' . ' ' . htmlentities($contour->title) . ''; $contours[] = $polygon; } } $alignClass = $atts['align'] ? 'active-contour-aligmnent-' . $atts['align'] : ''; ob_start(); ?>
shortcode($attrs); } public function api() { register_rest_route('active-contour/v1', '/render', [ 'methods' => 'GET', 'callback' => function($request) { $id = intval($request['id']); $cid = sanitize_text_field($request['cid']); $group = sanitize_text_field($request['group']); $tags = sanitize_text_field($request['tags']); $size = sanitize_text_field($request['size']); $align = sanitize_text_field($request['align']); return ['html' => do_shortcode("[active_contour id=\"{$id}\" cid=\"{$cid}\" size=\"{$size}\" group=\"{$group}\" tags=\"{$tags}\" align=\"{$align}\"]")]; }, 'permission_callback' => function () { return is_user_logged_in() && current_user_can('edit_posts'); } ]); register_rest_route('active-contour/v1', '/data', [ 'methods' => 'GET', 'callback' => function($request) { $id = intval($request['id']); $meta = wp_get_attachment_metadata($id); if ($meta) { $data = get_post_meta($id, '_active_contours', true); return ['json' => $data, 'sizes' => $meta]; } else { return ['json' => '[]', 'sizes' => []]; } }, 'permission_callback' => function () { return is_user_logged_in() && current_user_can('edit_posts'); } ]); } }