Jump to content


  • Posts

  • Joined

  • Last visited

Everything posted by Hippocode

  1. Also watch the internal index or GUID of your object inside the reset events. If I remember correctly, when an object is inserted into a wall, a copy is actually made for insertion, removing the original object.
  2. I would also like to do this. I've been trying various options but none seem to work. Particularly using a background color. It's OR an image/color, OR text, not both in the sense I would like to use it. Usually I make the text grey to indicate it's not editable... and with the direct edit option you can now handle this by cell basis.
  3. VectorMEP contains additional tools to model pipes in your drawing. They are not as detailed as the extended library of digitalcarbon but they do benefit from being a dedicated tool that has proper snapping, auto creation of fittings and several display options in 2D and 3D. This example shows ducts, but the piping tools work exactly the same.
  4. I looked into my notes and found this list of events that occur on specific triggers. Looking at these events, it seems that to move an object to another layer it's destroyed and recreated after, or at least those states are triggered. The internal index of the object stays the same though. With the SDK you can actually catch that exception when looking at the data provided in the event 14, but I'm unsure if this is available in Vectorscript.
  5. I thought moving an object should return 1 (kMovedReset). Are you moving by property change or using Cut/Paste?
  6. I would believe those are just custom control points and not part of the default reshape tool. See "IProviderCursorHandles" for managing tool like behaviour on using those points. Or at least it would be possible using this route. Not sure what "kObjXPropInteractiveSizingInsert" can do.
  7. As there are a lot of possibilities in how subobjects are generated and can interact with eachother and parent objects this can be hard to do. Lets hope they find the time to help you out 😉
  8. You can't create your own event types, but you can tag along an existing one. I think you are overthinking this. What about using a parameter value to execute that specific action? That way, you only need to trigger the action by setting that record field/parameter to a specific value prior to resetting the object. Your object code can filter that value and decide what to do during the recalculate event. Setting that value and resetting the object can be done from anywhere as long as you have a refrence (HANDLE) to that object.
  9. Vectorscript based tools/menu commands only live within their own event. They can't catch other events unless they are triggered. You can do this with the SDK. You could still keep the XML export as a vectorscript and make a small SDK project that triggers a vectorscript/menu command on a specific event.
  10. May the Force be with you. 🤖
  11. In my case it was stored within the drag event sink itself, I believe you are trying something different. I would guess these two options could be useful? - Using "VWDataSerializerSimple" class to store the data directly into the object. I'm using this more and more because it's less work to maintain compared to records and fields. - In case the value is unique you could also store in permanent memory by using a VCOMImmediateImpl<IVWSingletonUnknown> interface. You could import/export into a custom interface which is accessible during the full execution of Vectorworks. Remember to handle document changes etc if you go this route.
  12. I think that is exactly what you should do. I'm using this in one of my tools in "OnDrag()" and it does the job.
  13. Try this: gSDK->EditObjectSpecial(hObject, kObjXPropSpecialEditEditGroup);
  14. Option 2 is probably the hardest because you have to be carefull with undo and manage all actions properly for two objects that are "bound" on a parametric base. There is another option 3. In the SDK you can choose if the recalculate event removes everything prior to recreation. The inner object would reside permanently in the outer. For simplicity option 1 is probably the best choice. It also depends of the interaction you need. Does the outer control the inner? Or in reverse order? If it's just a couple fields you could duplicate them in the parent object, which update the inner object with those same fields. This is probably the easiest method. If you use the option of the profile group, the benefit is that you can have a button in the info palette that opens the profile group and preselects the inner object, from that point the inner objec's palette is shown for direct access. On exit, you can have the parent object reset and use the new values. You can also extend the object info palette with custom fields that are linked to any object or data as long as you handle the conversion as you need to bypass the normal translation from/to, I'm doing this with several plug-ins combined with a selector widget (kWidgetSubSelection) that allows me to iterate several inner objects within the object info palette. This is great for a couple fields but those other options are probably better for larger palettes. Some small snippets for indication. bool IProvider_MEPComponent::OnWidgetChange(SShapePaneWidgetOnWidgetChange& data, bool& outNeedReset, bool& outChangeOk) else if(fWidgetsContainer.IsWidget(WidgetId, "ToggleLegs")) { outNeedReset = false; eventHandled = false; using namespace VectorWorks::Extension; if(fLegs.size() > 0) { IShapePaneWidgetAccess::ESubSelButton CurrentButton = data.fWidgetAccess->GetCurrentButton(); if(CurrentButton == IShapePaneWidgetAccess::ESubSelButton::eSubSelButton_CurrentButtonDown) { HighlightActiveLeg(&data); } else if(CurrentButton == IShapePaneWidgetAccess::ESubSelButton::eSubSelButton_NextButtonDown) { fNumActiveLeg++; HighlightActiveLeg(&data); } else if(CurrentButton == IShapePaneWidgetAccess::ESubSelButton::eSubSelButton_PrevButtonDown) { if(fNumActiveLeg == 0) { fNumActiveLeg = fLegs.size() -1; } else { fNumActiveLeg--; } HighlightActiveLeg(&data); } else if(CurrentButton == IShapePaneWidgetAccess::eSubSelButton_CurrentButtonUp || CurrentButton == IShapePaneWidgetAccess::eSubSelButton_NextButtonUp || CurrentButton == IShapePaneWidgetAccess::eSubSelButton_PrevButtonUp) { data.fWidgetAccess->ClearHighlightPoint(); data.fWidgetAccess->SetValueBool(data.fViewWidget, false); } } } You need to manage TransferValue in both directions for widgets not bound to default parameters of your object. In this case I'm referencing data from whichever inner object is the active selection from the previous selector widget. bool IProvider_MEPComponent::TransferValue(VectorWorks::Extension::SShapePaneWidgetTransferValue &data) { using namespace VectorWorks::Extension; bool handled = false; // For some widgets we need to switch over the record format to the active connector... if( fWidgetsContainer.IsWidget(data.fWidgetID, "Leg_DimensionType") || fWidgetsContainer.IsWidget(data.fWidgetID, "Leg_Length") || fWidgetsContainer.IsWidget(data.fWidgetID, "Leg_Shape") || fWidgetsContainer.IsWidget(data.fWidgetID, "Leg_Width") || fWidgetsContainer.IsWidget(data.fWidgetID, "Leg_Height") ) { if(fLegs.size() > 0) { sUserData_JunctionLeg* pLeg = &fLegs[fNumActiveLeg % fLegs.size()]; if(data.fTransferToView) { if(fWidgetsContainer.IsWidget(data.fWidgetID, "Leg_DimensionType")) { TXString strValue; strValue.itoa(pLeg->DimensionType); data.fWidgetAccess->SetValueString(data.fViewWidget, strValue); } if(fWidgetsContainer.IsWidget(data.fWidgetID, "Leg_Width"))data.fWidgetAccess->SetValueString(data.fViewWidget, pLeg->NominalWidth); if(fWidgetsContainer.IsWidget(data.fWidgetID, "Leg_Height"))data.fWidgetAccess->SetValueString(data.fViewWidget, pLeg->NominalHeight); if(fWidgetsContainer.IsWidget(data.fWidgetID, "Leg_Shape")) { TXString strShape; strShape.itoa(pLeg->Shape); data.fWidgetAccess->SetValueString(data.fViewWidget, strShape); } if(fWidgetsContainer.IsWidget(data.fWidgetID, "Leg_Length"))data.fWidgetAccess->SetValueReal(data.fViewWidget, pLeg->Length, IShapePaneWidgetAccess::eRealType_Coord); } else { if(fWidgetsContainer.IsWidget(data.fWidgetID, "Leg_DimensionType")) { pLeg->DimensionType = (VWFC::VWObjects::sUserData_JunctionLeg::eLegDimensions) data.fWidgetAccess->GetValueString(data.fViewWidget).atoi(); } if(fWidgetsContainer.IsWidget(data.fWidgetID, "Leg_Width"))pLeg->NominalWidth = data.fWidgetAccess->GetValueString(data.fViewWidget); if(fWidgetsContainer.IsWidget(data.fWidgetID, "Leg_Height"))pLeg->NominalHeight = data.fWidgetAccess->GetValueString(data.fViewWidget); if(fWidgetsContainer.IsWidget(data.fWidgetID, "Leg_Shape")) { pLeg->Shape = (eShape) data.fWidgetAccess->GetValueString(data.fViewWidget).atoi(); } if(fWidgetsContainer.IsWidget(data.fWidgetID, "Leg_Length"))pLeg->Length = data.fWidgetAccess->GetValueReal(data.fViewWidget, IShapePaneWidgetAccess::eRealType_Coord); // Save user data back into junction and reset it.. VWMEPJunctionObj JunctionObj(fhObject); JunctionObj.SaveLegs(fLegs); JunctionObj.ResetObject(); } } handled = true; }
  15. I think you can't use a function as a procedure? Try making it return to a boolean value instead. result := SetWallPres...
  16. Set/GetSavedSetting is by far the easiest option to use, but this is more commonly used for settings that surpass the scope of a drawing/document. If you need to remember these settings based on the active file you can also use a custom hidden record with a field for each setting you require.
  17. Make sure you are compiling with at least 10.15, target should be 10.13. If you still have an issue I find the easiest way to tackle this is to start fresh from a sample project and copy over your custom files and settings. Old projects can contain deprecated settings and after a while it's hard to find what is used and what not. Also make sure you compile generic to cover both ARM64 and Intel.
  18. https://developer.vectorworks.net/index.php/VS:GetObject Since the symbol definition is unique you can get the handle using its name. Be sure to verify the object type.
  19. It will be part of the VectorMEP 2022 release which should be available during September 2021.
  20. Yes this is only possible using the SDK. You could create a small SDK project that runs a specific python script on startup or any other event if you don't want to bring over the full script.
  21. You could have a script traverse all the rectangles in that layer, and number them in sequence for you. On default that would be stacking order, but you could also use coordinates to define the order instead, like relative position.
  22. Well how I see it this is about two main functions. You need to automate the creation of certain objects (layers, rooms, sheets) You need to update the data of these objects. I'd assume this external info could change during the project development like updating room names. The input for both could be based from Excell or a Worksheet (imported Excell) or any input dialog whatever fits your needs. Both can be done with Vectorscript. A lot can go wrong so It'll be trial and error to develop this. FYI, I'm working on a new plug-in that allows you to export object data (for instance all rooms with their name, custom parameters etc) to Excell. Modify or append this data in Excell and import it back into the drawing, updating all these objects with the new data.
  23. For a simple standard a basic file or template as mentioned earlier is the way to go. If you need it to be more flexible with additional data, or generic from a source like Excell yes a script is a good option. It should be possible iterating all the cells from a Worksheet and do something with it.
  24. Think of the OnCursorAction triggers as a tool, you need to start it before the others will be triggered. And it starts with triggering a controlpoint by clicking/hovering it I guess. The other functions just show the images and text and decide if they should be visible, those are triggered constantly probably. I believe non parametric control points have positive index values, parametric ones negative. you can mix the use of them but it's important to return true/false properly @ IsDefaultCursorHandled Sint32 Index = context->GetCustomPtClientID(); // Index of CP I believe there are a lot of inconsistencies in those interfaces. For me I know that ICursorHandleActionContext::GetObject not always returns the effective object (or didn't in the past). For that reason I keep a copy of the handle in the interface itself and update it whenever GetObject does return a value in any of those functions. This ensures the other functions know which object we are managing the controlpoints for regardless of the provided context.
  • Create New...